天天看點

iOS 多線程的管理

本文講建立分線程的方法。

  • 第一種:直接調用NSObject的方法
  • 第二種:利用NSThread建立新線程
  • 第三種:建立NSOperation,并加入到隊列NSOperationQueue中
  • 第四種:GCD (grand central dispatch)線程優化技術

第一種:直接調用NSObject的方法

所有的類都是NSObject的子類,是以都繼承了這些方法:

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
    // equivalent to the first method with kCFRunLoopCommonModes
           
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait);
    // equivalent to the first method with kCFRunLoopCommonModes
           

最後一個方法是在背景執行的,不要用這個方法去做修改ui的動作。

第二種:用NSThread建立新線程

1、利用NSThread的初始化方法建立新線程

建立後需要手動開啟線程。

2、用NSThread類的類方法建立新線程

建立後線程自動開啟。不論用哪種方法建立的線程,要保持線程安全要用到NSLock。這個NSLock,鎖的是在其lock方法到unlock方法之間的代碼,鎖定之後,這段代碼同一時間隻會被一個線程執行。

第三種:建立NSOperation,并加入到隊列NSOperationQueue中

NSOperationQueue會建立一定數量的線程來執行加入到其中的NSOperation,可以設定本隊列可同時執行的最大線程數maxConcurrentOperationCount,預設的話,最大線程數為NSOperationQueueDefaultMaxConcurrentOperationCount,這個數值會根據系統環境自動适應。

加入到隊列的NSOperation的并行執行。

如果需要指定順序,可以通過添加依賴。一個NSOperation對象,隻有當其依賴的NSOperation對象執行完,才會執行。

一個app可以有多個隊列,各個隊列分别管理各自的NSOperation對象。

NSOperation有許多子類,比如NSInnovationOperation、NSBlockOperation,這些子類的差別僅僅在于與動作綁定的方式不一樣,隊列會被同等對待它們。

第四種:GCD (grand central dispatch)

GCD是一個可以發揮多核處理器性能的任務管理技術。

GCD與NSOperationQueue比較像的地方是,它也有隊列,類型為dispatch_queue_t。和NSOperationQueue一樣,隊列管理着許多待執行的任務。

不同的是,GCD分為并行隊列和串行隊列。串行隊列會将加入其中的任務全部放到一個線程中執行,而并行隊列執行任務的思想和NSOperationQueue類似,會用多線程來執行任務。

1、建立隊列:

(1)直接擷取

主線程的運作也是有一個隊列在進行管理,這個隊列是main()方法執行的時候就已經建立了的,可以直接利用這個隊列,然後往裡面添加任務。要注意這個隊列是串行隊列。也就是說添加到主線程所在隊列的任務是由主線程完成的。

擷取主線程所在隊列:

dispatch_get_main_queue();
           

還有另外一個方法可直接擷取的隊列:

dispatch_get_global_queue();
           

GCD自動建立了三個全局隊列,并且都是并行的。三個隊列用優先級來區分。

(2)自己建立新隊列:

dispatch_queue_create(const char *label,dispatch_queue_attr_t attr);
           

第一個參數是隊列名字,第二個參數決定這個隊列是串行隊列還是并行隊列,其值為常量:

DISPATCH_QUEUE_SERIAL

或者

DISPATCH_QUEUE_CONCURRENT

2、往隊列中添加任務

任務很多時候以block的形式添加到隊列。

(1)添加block任務的兩個基本方法

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
           
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
           

兩個方法的第一個參數是隊列,第二個參數是要加入的block。

差別在于,第一個方法是異步方法,會将block加入到隊列之後就會馬上傳回;第二個方法是同步方法,會等到加入的block執行完以後才會傳回。

是以要注意,由于主線程所在隊列為串行隊列,如果使用同步方法往裡面加入的block,由于block搶不過主線程而一直得不到執行,但是不執行又不傳回,最終導緻應用程式卡死。也就是說,添加任務到串行隊列最好使用異步方法。

盡管串行隊列所管理的任務是在一個線程中串行執行的,但串行隊列和其他隊列之間則是并行執行的。

(2)添加延時block任務的方法:

dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);
           

不管加入的是串行隊列還是并行隊列,都會延時執行。

(3)添加block任務到隊列的時候标記分組

建立分組

添加block到隊列且标記分組

dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
           

标記了分組的任務和沒有标記分組的任務對于隊列來說是沒有差別的,标記分組影響的隻是任務相對于其他任務的執行次序。

(4)一些添加block任務的特殊方法。

方法一:

dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
           

用這個方法添加的block會成為路障:在它之前加入到隊列的任務都執行完,才輪到它執行,等它執行完以後,之後的任務才可以執行。這個方法隻對并行隊列生效。

方法二:

dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
           

執行這個方法時,block仍未加入隊列,隻是先登記了,要等執行這個方法之前已标記了group的任務都完成了,才會真正加入隊列。

驗證一下:

與添加block到隊列的基本方法綜合起來驗證這些特殊方法的效果(下圖):

代碼中建立了一個并行隊列,建立了一個分組。

然後往這個隊列中加入了若幹block,按方法的執行順序命名為任務1~任務7。

任務1和任務4标記了分組,其他沒有标記,任務3為路障,任務6先要等标記了相同分組的任務完成後才會加入隊列。任務之間原則上并行執行。

iOS 多線程的管理

運作結果:

iOS 多線程的管理

從運作結果可以看到,

任務1和任務2在任務3(路障)之前加入到隊列,是以都在任務3之前執行完畢。

任務4、5、7是并行執行的。

任務6等标記了相同分組的任務4完成後才加入隊列。