知識梳理(多執行緒)
多執行緒:一個程序裡面開啟多條執行緒,每條執行緒可以單獨的執行不同的任務。
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;
還有三個方法終點說一下
- maxConcurrentOperationCount 設定大的併發數
- suspended 暫停任務
- cancelAllOperations 取消所有任務,這裡也是隻能取消當前沒有開始的任務。(要想取消當前的任務,需要在任務裡隨時判斷isCanceled變數)
通過上面的總結,希望能回答兩個問題(1、iOS實現多執行緒的方式,各自的特點,優缺點 2,多執行緒使用要注意什麼,gcd為什麼會造成死鎖?)。