iOS - 多線程

多線程:一個進程裏面開啓多條線程,每條線程可以單獨的執行不同的任務。

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爲什麼會造成死鎖?)。

 

多線程應用的好,可以提升app的運行效率,流暢度,運用不好的話也會有許多的負面效果,比如開啓線程是很消耗系統資源的,不能無限制開啓,再比如死鎖等問題。所以以後在項目裏要靈活運用多線程。

(最近就遇到一個問題,搜索列表問題,隨着用戶的輸入,實時顯示搜索到的列表(本地數據,實時過濾,當然量比較大),如果只是運用gcd的get_global_queue實現多線程的話會有問題,因爲任務回調回來的時機不同,會造成顯示的列表不對。然後我就用了Operation來解決的這個問題,當來新的搜索任務也就是用戶輸入改變的時候,如果上個任務還沒有完成,則取消之前的任務。)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章