周末梳理了一下iOS幾種多線程的方案,總結下平時GCD的主要用法和場景。
一、iOS常見的多線程方案
(1) Pthreads: 基于C語言的框架,在多種操作系統(tǒng)上都有使用,要手動地去管理生命周期,所以在iOS平常的開發(fā)中并不常用。
(2) NSThread: Apple封裝后的線程對象,常用的幾種簡單的獲取線程,啟動取消線程等方法。開發(fā)中只有一些簡單的場景例如[NSThread currentThread], performSelectorOnMainThread:才會使用,因為它在解決問題時還不夠智能,要手動地去管理狀態(tài)。
(3) GCD: Apple為并行計算提出的解決方案,優(yōu)點在于它會自動管理線程的生命周期。同時它使用了Block來讓使用更加方便。
(4) NSOperation & NSOperationQueue: Apple對GCD進行了更多一層的封裝,提供了更多的接口來進行任務的管理。
二、GCD
(1) GCD中兩個重要的基本概念:隊列和任務
* 隊列:
一種遵循FIFO來存放任務的隊列,GCD中有兩種類型:串行隊列(Serial)和并行隊列(Concurrent)
串行隊列:對應一個線程,讓任務一個接一個地執(zhí)行。系統(tǒng)默認提供main_queue,并規(guī)定UI只能在主線程中操作,避免產(chǎn)生混亂
并行隊列:可能對應多個線程,可以讓多個任務同時執(zhí)行。系統(tǒng)默認提供global_queue
我們自己可以創(chuàng)建串行和并行隊列
* 任務:
就是需要執(zhí)行的一段代碼,GCD是放在Block里的。執(zhí)行任務有兩種方式同步執(zhí)行(async)和異步執(zhí)行(async)
如果是同步執(zhí)行,當前任務會阻塞當前線程并等待Block中的任務執(zhí)行完成才繼續(xù)下一個任務
如果是異步執(zhí)行,當前任務不會阻塞等待,會直接往下執(zhí)行。
"放到并行隊列的異步(async)任務,GCD 也會?FIFO的取出來,但不同的是,它取出來一個就會放到別的線程,然后再取出來一個又放到另一個的線程。"
例1:阻塞主線程

分析:這里在主線程發(fā)起了一個主線程中的同步任務,當要開始執(zhí)行的時候,dispatch_sync阻塞了主線程,并嘗試把block中的任務放到主線程中執(zhí)行,但這時候主線程已經(jīng)被阻塞,所以任務無法完成,造成死鎖。
同理:

例2:asyn和async的任務決定了什么?

從以上的結果看來,queue只是管理任務的一個方式,無法決定執(zhí)行的某一個線程,但是能決定任務順序以及是否開辟新的線程。
(2) GCD常用的幾個并行隊列中線程同步的方法:Group、Barrier、Semaphore
dispatch_barrier_async/sync
在GCD中起到一個柵欄的作用,同Concurrent Dispatch Queue隊列一起使用。它等待所有位于barrier函數(shù)之前的操作執(zhí)行完畢后執(zhí)行,并且在barrier函數(shù)執(zhí)行之后,barrier函數(shù)之后的操作才會得到執(zhí)行。
barrier的async和sync的區(qū)別只是,是否在執(zhí)行結束才插入別的任務。但是都會保證同時這個隊列只有這個任務在執(zhí)行
假設有一塊資源需要同步讀異步寫,則可以使用barrier函數(shù)來保證寫操作之前的讀操作全部完成后,才進行寫。不會有兩次讀取的值不一樣的問題。
dispatch_group
可以把任務分組,打包成為一個大的任務,dispatch_group_notify來保證前面的異步/同步任務都結束后才回到某個線程。但是無法保證其中異步任務執(zhí)行的順序
dispatch_semaphore
有時候線程過多,需要控制Concurrent Queue中運行的線程數(shù)量,就可以使用semaphore。而以下的這些在Serial Queue中是不生效的,因為Serial Queue中始終一次只能執(zhí)行一個任務
1. dispatch_semaphore_create?創(chuàng)建一個semaphore
2. dispatch_semaphore_signal?發(fā)送一個信號
3. dispatch_semaphore_wait?等待信號

得出的下面結果看到,semaphore可以控制同時執(zhí)行的線程數(shù)量,換言之也可以用來保證線程的同步。

假設把信號量改成2,則可以看到有兩個任務可以同時進行

用信號量可以實現(xiàn)兩個線程之間的依賴關系

(3)dispatch_once 和?dispatch_after,dispatch_suspend
dispatch_once?
常見的單例創(chuàng)建方法
dispatch_after
dispatch_after是來延遲執(zhí)行的GCD方法,因為在主線程中我們不能用sleep來延遲方法的調(diào)用,所以用dispatch_after是最合適的,dispatch_after的真正含義是在6秒后把任務添加進隊列中,并不是表示在6秒后執(zhí)行。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2?*?NSEC_PER_SEC)),?dispatch_get_main_queue(), ^{
?NSLog(@"after 2 seconds %@",[NSThread?currentThread]);
?});
dispatch_suspend
suspend不會立即暫停正在運行的block,而是在當前block執(zhí)行完成后,暫停后續(xù)的block執(zhí)行。
(4)線程之間通信

(5)如何停止一個GCD中正在運行的線程
用一個全局變量或是property來控制
@property(nonatomic, assign)BOOL shouldCancel;
@property(nonatomic, strong)dispatch_queue_t conQueue;
-?(void)viewDidAppear:(BOOL)animated? {??
????[super?viewDidAppear:animated];??
????self.conQueue =?dispatch_get_global_queue(0,?0);??
????dispatch_async(self.conQueue,?^{??
????????while?(!self.shouldCancel)?{??
????????????????sleep(1);??
? ? ? ? };??
????});??
}??
-?(void)closeThread? {??
if?(self.conQueue)?{??
????????self.shouldCancel?=?YES;??
????????dispatch_suspend(q);??
????????dispatch_release(q);??
? ? ? ? self.conQueue?=?nil;??
????}??
}??
Reference
http://www.itdecent.cn/p/0b0d9b1f1f19
http://www.itdecent.cn/p/2d57c72016c6
http://www.itdecent.cn/p/cfcc0c302621
http://blog.csdn.net/linfengwenyou/article/details/48948355