關于iOS多線程淺析

多線程(英語:multithreading)星澳,是指從軟件或者硬件上實現(xiàn)多個線程并發(fā)執(zhí)行的技術。具有多線程能力的計算機因有硬件支持而能夠在同一時間執(zhí)行多于一個執(zhí)行緒旱易,進而提升整體處理性能禁偎。 —— 維基百科

  • 多線程概念
    • 概念
    • 例子
    • 目的
    • 優(yōu)缺點
  • 多線程的同步異步
    • 同步
    • 異步
  • 多線程的線程進程
    • 線程
    • 進程
  • 多線程的方式
    • PThread
    • NSThread
    • GCD
      • GCD的概念
      • GCD的簡單使用
      • GCD的任務和隊列
    • NSOPeration
      • NSOperation的簡介
      • NSOperation的簡單使用
      • NSOperation的高級功能
  • 面試題

多線程的概念

  • 概念

    • 多線程:一個進程中可以開啟多條線程,多條線程可以同時執(zhí)行不同的任務阀坏。
  • 例子

    • 舉個例子:酷我音樂的邊下載邊聽歌,迅雷的邊下載邊播放如暖。
  • 多線程目的

    • 將耗時操作放在后臺處理,保證UI界面的正常顯示和交互忌堂。
    • 網(wǎng)絡操作是非常耗時的盒至,在做網(wǎng)絡開發(fā),所有網(wǎng)絡訪問都是耗時操作.需要在后臺線程中執(zhí)行士修。
    • 多線程開發(fā)的原則:越簡單越好枷遂。
  • 多線程優(yōu)缺點

    • 優(yōu)點:能‘適當’提高程序的執(zhí)行效率,能適當提高CPU的內(nèi)存利用率棋嘲,線程上的任務執(zhí)行完成后,線程會自動銷毀節(jié)省內(nèi)存酒唉。
    • 缺點:如果開啟線程過多會占用大量CPU資源降低程序性能。

同步異步(同步和異步是兩種執(zhí)行任務的方式沸移。)

  • 同步

    • 同步:代碼從上到下順序執(zhí)行就叫做同步執(zhí)行(多個任務依次執(zhí)行)痪伦。
  • 異步

    • 異步:多個任務同時執(zhí)行就是異步執(zhí)行。

多線程的線程進程

  • 進程

    • 進程:在系統(tǒng)中正在運行的一個程序叫做進程雹锣,進程可以類比成正在正常運營的公司流妻。
  • 線程

    • 線程:線程可以類比成公司里的員工,程序啟動默認會開啟一個線程笆制。

多線程的方式

  • PThread(幾乎不用)

    • Pthreads定義了一套C語言的類型绅这、函數(shù)與常量,它以pthread.h頭文件和一個線程庫實現(xiàn)在辆。
  • NSThread(很少使用)

    • NSThread是基于線程使用证薇,輕量級的多線程編程方法(相對GCD和NSOperation)度苔,一個NSThread對象代表一個線程,需要手動管理線程的生命周期浑度,處理線程同步等問題寇窑。
  • GCD(經(jīng)常使用)

    • GCD的概念
      • 什么是GCD:全稱是Grand Central Dispatch,純C語言的箩张,提供了非常多強大的函數(shù)甩骏。
      • GCD的核心:將任務添加到隊列。
      • GCD使用的兩個步驟:創(chuàng)建任務先慷,確定要做的事情饮笛,GCD中的任務是使用BLOCK封裝的。將任務添加到隊列中论熙,GCD會自動將隊列中的任務取出福青,放到對應的線程中執(zhí)行。任務的取出遵循隊列的FIFO原則 : 先進先出,后進后出
    • GCD的簡單使用
      • 任務添加到隊列
        - (void)GCDDemo1 {
            // 1. 創(chuàng)建隊列
            dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
            // 2. 創(chuàng)建任務 : 用block指定的 (無參無返回值的)
            void (^task)() = ^ {
                NSLog(@"%@",[NSThread currentThread]);
            };
            // 3. 把任務添加到隊列
            // dispatch_async : 表示任務是異步的
            // dispatch_sync : 表示任務是同步的
            dispatch_async(queue, task);
        }   
        
        
      • 簡寫
            - (void)GCDDemo2 {
                dispatch_async(dispatch_get_global_queue(0, 0), ^{
                NSLog(@"%@",[NSThread currentThread]);
                });
            }
        
        
      • 線程間的通信
            - (void)GCDDemo4 {
                dispatch_async(dispatch_get_global_queue(0, 0), ^{
                    NSLog(@"假裝在努力下載...%@",[NSThread currentThread]);
                    // 下載結束之后,回到主線程更新UI
                    dispatch_async(dispatch_get_main_queue(), ^{
                    NSLog(@"假裝在更新UI...%@",[NSThread currentThread]);
                    });
                });
            }
        
        
      • 使用GCD的線程間的通信實現(xiàn)異步下載網(wǎng)絡圖片
- (void)downloadImage
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        NSLog(@"downloadImage %@",[NSThread currentThread]);
        
        // URL
        NSURL *URL = [NSURL URLWithString:@"http://atth.eduu.com/album/201203/12/1475134_1331559643qMzc.jpg"];
        // data
        NSData *data = [NSData dataWithContentsOfURL:URL];
        // image
        UIImage *image = [UIImage imageWithData:data];
        
        // 拿到圖片對象之后,回到主線程刷新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            
            NSLog(@"updateUI %@",[NSThread currentThread]);
            
            self.myImageView.image = image;
            [self.myImageView sizeToFit];
            [self.myScrollView setContentSize:image.size];
        });
    });
}

  • GCD的任務和隊列
    • GCD的任務:
      • 同步的方式執(zhí)行任務 : 在當前線程中依次執(zhí)行任務脓诡。 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
      • 異步的方式執(zhí)行任務 : 新開線程在新線程中執(zhí)行任務无午。
        dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
    • GCD隊列:
      • 串行隊列:讓任務一個接著一個有序的執(zhí)行:不管隊列里面放的是什么任務,一個任務執(zhí)行完畢后祝谚,再執(zhí)行下一個任務宪迟,同時只能調(diào)度一個任務執(zhí)行。
      • 并發(fā)隊列:可以讓多個任務并發(fā)/同時執(zhí)行交惯,自動開啟多個線程同時執(zhí)行多個任務次泽,同時可以調(diào)度多個任務執(zhí)行。并發(fā)隊列的并發(fā)功能只有內(nèi)部的任務是異步任務時商玫,才有效。
  • 代碼小結

串行隊列+同步任務

/*
 1.不開線程
 2.有序執(zhí)行
*/
- (void)GCDDemo1
{
    /* 
     創(chuàng)建串行隊列
     參數(shù)1 : 隊列的標識符
     參數(shù)2 :  隊列的屬性,決定了隊列是串行的還是并行的
     DISPATCH_QUEUE_SERIAL : 串行隊列
    */
    dispatch_queue_t queue = dispatch_queue_create("GL", DISPATCH_QUEUE_SERIAL);
    
    // 循環(huán)的創(chuàng)建了10個同步任務,添加到隊列
    for (NSInteger i = 0; i<10; i++) {
        
        // 把同步任務添加到串行隊列
        dispatch_sync(queue, ^{
            NSLog(@"%zd %@",i,[NSThread currentThread]);
        });
    }
    
    NSLog(@"哈哈哈");
}

串行隊列+異步任務

- (void)GCDDemo2
{
    // 串行隊列
    dispatch_queue_t queue = dispatch_queue_create("GL", DISPATCH_QUEUE_SERIAL);
    
    for (NSInteger i = 0; i<10; i++) {
        // 把異步任務添加到串行隊列
        dispatch_async(queue, ^{
            NSLog(@"%zd %@",i,[NSThread currentThread]);
        });
    }
    
    NSLog(@"嘿嘿嘿");
}

并行隊列+同步任務

/*
 不開線程
 有序執(zhí)行
 */
- (void)GCDDemo1
{
    // 創(chuàng)建并行隊列
    // DISPATCH_QUEUE_CONCURRENT : 并行隊列
    // 并行隊列只能決定"是否"可以同時調(diào)度多個任務;不能決定開不開線程
    dispatch_queue_t queue = dispatch_queue_create("GL", DISPATCH_QUEUE_CONCURRENT);
    
    for (NSInteger i = 0; i<10; i++) {
        // 把同步任務添加到并行隊列
        dispatch_sync(queue, ^{
            NSLog(@"%zd %@",i,[NSThread currentThread]);
        });
    }
    
    NSLog(@"哈哈哈");
}

并行隊列+異步任務

/*
 開線程
 無序執(zhí)行
 */
- (void)GCDDemo2
{
    // 并行隊列
    dispatch_queue_t queue = dispatch_queue_create("GL", DISPATCH_QUEUE_CONCURRENT);
    
    for (NSInteger i = 0; i<10; i++) {
        
        // 把異步任務添加到并發(fā)隊列
        dispatch_async(queue, ^{
            NSLog(@"%zd %@",i,[NSThread currentThread]);
        });
    }
    
    NSLog(@"嘿嘿嘿");
}
Paste_Image.png
  • NSOperation(經(jīng)常使用)

    • NSOperation的簡介
      • 是OC語言中基于GCD的面向對象的封裝牡借,使用起來比GCD更加簡單拳昌。提供了一些GCD不好實現(xiàn)的功能,蘋果推薦使用钠龙。NSOperation還不用關心線程和線程的聲明周期炬藤。
      • NSOperation是個抽象類無法直接使用。因為方法只有聲明沒有實現(xiàn)碴里。
      • 子類:NSInvocationOperation和NSBlockOperation沈矿,自定義NSOperation操作默是異步的。
      • 隊列 : NSOperationQueue隊列默認是并發(fā)的咬腋。
      • 核心:GCD的核心 : 將任務添加到隊列中羹膳。OP的核心 : 將操作添加到隊列中。
    • NSOperation的簡單使用
      • 先將需要執(zhí)行的操作封裝到一個NSOperation對象中根竿,創(chuàng)建NSOperation對象陵像。
      • 將NSOperation對象添加到NSOperationQueue中就珠。
      • NSOperationQueue會自動將NSOperation取出來
      • 將取出的NSOperation封裝的操作自動放到一條對應的新線程中執(zhí)行。
    • NSOperation的高級功能
      • 最大并發(fā)數(shù)

設置最大并發(fā)數(shù)

// 隊列的最大并發(fā)數(shù)的屬性
// 作用 : 控制隊列同時調(diào)度任務執(zhí)行的個數(shù);
// 間接控制了線程的數(shù)量;
// 注意 : 隊列的最大并發(fā)數(shù),不是線程數(shù);

@implementation ViewController {
    
    /// 全局隊列
    NSOperationQueue *_queue;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _queue = [[NSOperationQueue alloc] init];
    
    // 設置隊列的最大并發(fā)數(shù) : 至少開兩個
    _queue.maxConcurrentOperationCount = 2;
}

演示

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self GCDDemo];
}

- (void)GCDDemo
{
    for (NSInteger i = 0; i<50; i++) {
        
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"%zd %@",i,[NSThread currentThread]);
        }];
        
        [_queue addOperation:op];
    }
}

執(zhí)行結果:任務是兩個兩個的執(zhí)行醒颖。

繼續(xù)妻怎、暫停、取消全部
  • 在最大并發(fā)數(shù)代碼的基礎上增加暫停泞歉、繼續(xù)逼侦、取消。
#pragma 取消全部
/*
 1.正在執(zhí)行的操作無法被取消;
 2.如果非要取消正在執(zhí)行的操作,需要自定義NSOperation
 3.這個取消全部的操作有一定的時間延遲
 */
- (IBAction)cancelAll:(id)sender
{
    // 移除隊列里面"所有"的操作
    [_queue cancelAllOperations];
    
    NSLog(@"取消全部 %tu",_queue.operationCount);
}

#pragma 繼續(xù)
- (IBAction)jixu:(id)sender
{
    // 不掛起隊列,使隊列繼續(xù)調(diào)度任務執(zhí)行
    _queue.suspended = NO;
    
    NSLog(@"繼續(xù) %tu",_queue.operationCount);
}

#pragma 暫停
/*
 1.正在執(zhí)行的操作無法被暫停
 2.operationCount : 隊列里面的操作個數(shù);統(tǒng)計的是隊列里面還沒有執(zhí)行完的操作;
 3.隊列里面的任務一旦執(zhí)行完,會從隊列里面移除;
 */
- (IBAction)zanting:(id)sender
{
    // 掛起隊列,使隊列暫停調(diào)度任務執(zhí)行
    _queue.suspended = YES;
    
    NSLog(@"暫停 %tu",_queue.operationCount);
}
  • 暫停隊列結論

將隊列掛起之后,隊列中的操作就不會被調(diào)度腰耙,但是正在執(zhí)行的操作不受影響榛丢。operationCount:操作計數(shù),沒有執(zhí)行和沒有執(zhí)行完的操作沟优,都會計算在操作計數(shù)之內(nèi)涕滋。注意:如果先暫停隊列,再添加操作到隊列,隊列不會調(diào)度操作執(zhí)行挠阁。所以在暫停隊列之前要判斷隊列中有沒有任務宾肺,如果沒有操作就不暫停隊列。

  • 取消隊列結論

一旦調(diào)用的 cancelAllOperations方法侵俗,隊列中的操作锨用,都會被移除,正在執(zhí)行的操作除外。 正在執(zhí)行的操作取消不了隘谣,如果要取消,需要自定義NSOperation寻歧。 隊列取消全部操作時掌栅,會有一定的時間延遲。

  • 操作間依賴關系码泛。

場景:登陸-->付費-->下載-->通知用戶

準備需要執(zhí)行的操作

    // 登錄
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"登錄 %@",[NSThread currentThread]);
    }];
    
    // 付費
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"付費 %@",[NSThread currentThread]);
    }];
    
    // 下載
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"下載 %@",[NSThread currentThread]);
    }];
    
    // 通知用戶
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"通知用戶 %@",[NSThread currentThread]);
    }];

添加依賴(核心代碼)

    /*
     添加依賴關系
     1.不能在操作添加到隊列之后,在建立依賴關系;因為已經(jīng)晚了
     2.可以跨隊列建立依賴關系
     3.不能建立循環(huán)依賴
     */
    [op2 addDependency:op1]; // 付費依賴登錄
    [op3 addDependency:op2]; // 下載依賴付費
    [op4 addDependency:op3]; // 通知用戶依賴下載
    
    // [op1 addDependency:op4]; // 登錄依賴通知用戶 : 循環(huán)依賴;會卡死
    
    // 批量把操作添加到隊列
    // waitUntilFinished : 是否等待前面的異步任務執(zhí)行完,在執(zhí)行后面的代碼
    [_queue addOperations:@[op1,op2,op3] waitUntilFinished:NO];
    
    // 一個操作不能同時添加到兩個隊列
    [[NSOperationQueue mainQueue] addOperation:op4];
  • 結論

    不能循環(huán)建立操作間依賴關系猾封,否則隊列不調(diào)度操作執(zhí)行。

    操作間可以跨隊列建立依賴關系噪珊。

    要將操作間的依賴建立好了之后,再添加到隊列中晌缘,先建立操作依賴關系,再把操作添加到隊列痢站。

面試題

  • 面試題僅供參考

    用 NSOPeration 和 NSOpertionQueue 處理 A,B,C 三個線程,要求執(zhí)行完 A,B 后才能執(zhí)行 C, 怎么做磷箕?

    添加操作依賴,C依賴于A同時依賴于B阵难。創(chuàng)建操作隊列岳枷,將操作添加到操作隊列中。

    線程間怎么通信?

    什么是線程通信:不同線程之間傳遞數(shù)據(jù)嫩舟,一般線程傳遞到主線程氢烘。 iOS中開啟多線程的方式:三種。比如:在子線程下載圖片家厌,然后回到主線程顯示圖片播玖。

    簡述多線程的作用以及什么地方會用到多線程?OC實現(xiàn)多線程的方法有哪些?談談多線程安全問題的幾種解決方案?何為線程同步,如何實現(xiàn)的?分線程回調(diào)主線程方法是什么,有什么作用?

    1. 耗時操作、界面卡死的時候使用多線程

    2. 作用:可以同時執(zhí)行多個任務饭于,適當提高程序的執(zhí)行效率蜀踏。為了提高CPU的使用率,采用多線程的方式去同時完成幾件事互不干擾掰吕。

    3. iOS中多線程的方法:NSThread果覆、NSOperation、GCD殖熟、pthread

    4. 使用場景:同時上傳和下載多個文件:加載網(wǎng)絡數(shù)據(jù)同時展示Loading的UI局待、大量數(shù)據(jù)I/O操作。

    5. 資源共享造成的安全問題:多線程環(huán)境下菱属,當多個線程同時操作共享資源的setter和getter方法時钳榨,會造成數(shù)據(jù)的讀寫錯亂就是線程安全問題。

    6. 線程同步技術:使多個線程一次有序的執(zhí)行纽门,實現(xiàn)方案是加鎖薛耻,把共享資源的讀寫操作鎖起來常用的是互斥鎖。

    7. 線程間的通信:一個線程執(zhí)行完任務之后赏陵,把執(zhí)行的結果傳遞到另外一個線程叫線程間通信饼齿。線程間通信可用來在兩個線程間傳遞數(shù)據(jù)。


感謝讀到最后的朋友蝙搔,最后請點贊支持一下缕溉,謝謝!

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吃型,一起剝皮案震驚了整個濱河市证鸥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌败玉,老刑警劉巖敌土,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件镜硕,死亡現(xiàn)場離奇詭異运翼,居然都是意外死亡,警方通過查閱死者的電腦和手機兴枯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門血淌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贷笛,“玉大人,你說我怎么就攤上這事隐孽【刮龋” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵沦补,是天一觀的道長乳蓄。 經(jīng)常有香客問我,道長夕膀,這世上最難降的妖魔是什么虚倒? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮产舞,結果婚禮上魂奥,老公的妹妹穿的比我還像新娘。我一直安慰自己易猫,他們只是感情好耻煤,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著准颓,像睡著了一般哈蝇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瞬场,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天买鸽,我揣著相機與錄音,去河邊找鬼贯被。 笑死眼五,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的彤灶。 我是一名探鬼主播看幼,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼幌陕!你這毒婦竟也來了诵姜?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤搏熄,失蹤者是張志新(化名)和其女友劉穎棚唆,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體心例,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡宵凌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了止后。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞎惫。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡溜腐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瓜喇,到底是詐尸還是另有隱情挺益,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布乘寒,位于F島的核電站望众,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏伞辛。R本人自食惡果不足惜黍檩,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望始锚。 院中可真熱鬧刽酱,春花似錦、人聲如沸瞧捌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姐呐。三九已至殿怜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間曙砂,已是汗流浹背头谜。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鸠澈,地道東北人柱告。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像笑陈,于是被迫代替她去往敵國和親际度。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容