1. 程式人生 > >知識梳理(多執行緒)

知識梳理(多執行緒)

多執行緒:一個程序裡面開啟多條執行緒,每條執行緒可以單獨的執行不同的任務。

iOS實現多執行緒的方式:

1、pthread(C寫的、基本不用) 2、NSThread 3、gcd 4、NSOperation

下面分別介紹下後三個常用的多執行緒方式

NSThread:

使用方式

// 方式1
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
[thread start];// 開啟

// 方式2
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

// 方式3
[self performSelectorInBackground:@selector(run) withObject:nil];

介紹NSThread,要首先介紹一下執行緒的生命週期:新建-就緒-執行-阻塞-死亡

優點:比較輕量,使用方式更加靈活,可以很直觀的控制執行緒物件。例如直接取消執行緒,也可以自定執行緒。

缺點:需要自己管理執行緒的生命週期、執行緒同步。

解釋一下執行緒同步:多條執行緒按順序執行任務。NSThread通過加鎖實現,加鎖對系統資源有一定的消耗。

下面的兩種方式不用關心執行緒管理,資料同步的問題。

GCD:

使用方式

dispatch_queue_t queue = dispatch_queue_create("queue.concurrent", DISPATCH_QUEUE_CONCURRENT); // 序列佇列

dispatch_queue_t queue = dispatch_queue_create("queue.serial", DISPATCH_QUEUE_SERIAL); //並行佇列

dispatch_queue_t queue =  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//獲取全域性的併發佇列

dispatch_queue_t  queue = dispatch_get_main_queue();//獲取主佇列 

dispatch_sync(queue, ^{ // 同步函式 要求馬上執行
    //do something        
});

dispatch_async(queue, ^{ // 非同步函式 等主執行緒執行完,在開執行緒執行任務
    // do something
});

GCD是蘋果為多核並行運算提出的解決方案,會自動利用多核,自動管理執行緒的生命週期(建立,排程,銷燬)。

那麼GCD是如何自己管理生命週期和執行緒同步的問題呢,有兩個概念 佇列(queue) 和 任務 (task,上面的block),

使用方式已經在上面列出,下面總結一下兩個函式和各種佇列的使用效果:

首先是同步函式dispatch_sync,無論是並行佇列還是序列佇列,都不會開啟新執行緒,並同步執行。

非同步函式dispatch_async在序列(非主佇列)或並行佇列中都會開啟新執行緒,不同的是序列佇列裡是序列執行任務,非同步佇列是併發執行任務。如果是在主佇列,不開啟新執行緒,序列執行任務。

 下面說一下死鎖的問題:

dispatch_sync有個特性是不等當前任務執行完成立即開啟下個任務,如果下個任務還是在當前佇列執行任務,就會造成相互等待(死鎖)。

舉個例子

// 當前在主佇列裡

dispatch_queue_t queue =  dispatch_get_main_queue(); // 獲取主佇列

dispatch_sync(queue, ^{ 
       NSLog(@"---download1---%@",[NSThread currentThread]);
});
//同步執行任務,這時候主佇列停止,等待sync新增的任務,
而sync新增的任務是在當前佇列裡執行NSLog,
NSLog又要等當前的佇列執行完上個任務才能執行,就陷入了相互等待。。。

執行緒之間通訊:

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
        // 非同步執行任務
      dispatch_async(dispatch_get_main_queue(), ^{ // 回到主佇列
            NSLog(@"%@",[NSThread currentThread]);
      });
}); 

取消任務

iOS8之後可以呼叫dispatch_block_cancel來取消(需要注意必須用dispatch_block_create建立dispatch_block_t) 

dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);

dispatch_block_t block = dispatch_block_create(0, ^{
        NSLog(@"block1 %@",[NSThread currentThread]);
});
dispatch_async(queue, block); 
dispatch_block_cancel(block);

需要注意的是這種方式只能取消還沒開始的任務

第二種取消方式就是模仿NSOperation裡面的isCanceled。就是執行任務的時候加判斷

dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);

__block BOOL isCancel = NO;

dispatch_async(queue, ^{
    sleep(3);
    if(isCancel){
        // 任務取消了
    }else{
        // 沒有取消,繼續執行
    }
});

 NSOperation

NSOperation是對gcd的封裝,面向物件,並多了一些簡單的功能。

NSOperation和NSOperationQueue實現多執行緒的具體步驟 1.將需要執行的操作封裝到一個NSOperation物件中 2.將NSOperation物件新增到NSOperationQueue中 系統會自動將NSOperationQueue中的NSOperation取出來,並將取出的NSOperation封裝的操作放到一條新執行緒中執行

NSOperation是個抽象類,要想使用必須繼承該類,系統提供了兩個直接能用的類

NSInvocationOperation

NSInvocationOperation *op  = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil];
[op start];

NSBlockOperation

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
       NSLog(@"@",[NSThread currentThread]); 
}];
[op addExecutionBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
[op start];

 自定義類例如 MyOperation 繼承NSOperation 

實現main方法

NSOperationQueue:

NSOperation可以呼叫start方法來執行任務,但預設是同步執行的 如果將NSOperation新增到NSOperationQueue(操作佇列)中,系統會自動非同步執行NSOperation中的操作

下面是新增到queue的方法

- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;

還有三個方法終點說一下

  1. maxConcurrentOperationCount 設定大的併發數
  2. suspended 暫停任務
  3. cancelAllOperations 取消所有任務,這裡也是隻能取消當前沒有開始的任務。(要想取消當前的任務,需要在任務裡隨時判斷isCanceled變數)

通過上面的總結,希望能回答兩個問題(1、iOS實現多執行緒的方式,各自的特點,優缺點 2,多執行緒使用要注意什麼,gcd為什麼會造成死鎖?)。