同步/異步
同步:多個(gè)任務(wù)情況下,一個(gè)任務(wù)A執(zhí)行結(jié)束,才可以執(zhí)行另一個(gè)任務(wù)B。只存在一個(gè)線程也就是主線程。
異步:多個(gè)任務(wù)情況下,一個(gè)任務(wù)A正在執(zhí)行,同時(shí)可以執(zhí)行另一個(gè)任務(wù)B。任務(wù)B不用等待任務(wù)A結(jié)束才執(zhí)行。存在多條線程。
圖1 同步示例圖
圖2異步示例圖
并發(fā)/并行
并行:指兩個(gè)或多個(gè)時(shí)間在同一時(shí)刻發(fā)生。多核CUP同時(shí)開啟多條線程供多個(gè)任務(wù)同時(shí)執(zhí)行,互不干擾。
并發(fā):指兩個(gè)或多個(gè)事件在同一時(shí)間間隔內(nèi)發(fā)生。可以在某條線程和其他線程之間反復(fù)多次進(jìn)行上下文切換,看上去就好像一個(gè)CPU能夠并且執(zhí)行多個(gè)線程一樣。其實(shí)是偽異步。如下并發(fā)圖,在同一線程,任務(wù)A先執(zhí)行了20%,然后A停止,任務(wù)B重新開始接管線程開始執(zhí)行。
圖3 并行示意圖
圖4 并發(fā)示意圖
好了,簡(jiǎn)單談了一下多線程可能涉及到的的概念,看官先消化一下,再接著往下看。
多線程是指在軟件或硬件上實(shí)現(xiàn)多個(gè)線程并發(fā)執(zhí)行的技術(shù)。通俗講就是在同步或異步的情況下,開辟新線程,進(jìn)行線程間的切換,以及對(duì)線程進(jìn)行合理的調(diào)度,做到優(yōu)化提升程序性能的目的。那么多線程編程會(huì)帶給我們很多好處,同時(shí)也是一種容發(fā)生各種問題的編程技術(shù)。比如多個(gè)線程更新相同的資源會(huì)導(dǎo)致數(shù)據(jù)的不一致[數(shù)據(jù)競(jìng)爭(zhēng)],停止等待時(shí)間的線程會(huì)導(dǎo)致多個(gè)線程相互持續(xù)等待[死鎖],使用太多線程會(huì)消耗大量?jī)?nèi)存。
圖5 多線程編程的問題
盡管多線程編程極易發(fā)生各種問題,但是呢也應(yīng)當(dāng)使用多線程編程,這是為什么呢?因?yàn)槭褂枚嗑€程編程可以保證應(yīng)用程序的相應(yīng)性能。利大于弊,小心使用即可。
應(yīng)用程序在啟動(dòng)時(shí),在主線程中描繪用戶界面,處理觸摸屏幕的事件等。如果在該主線程中進(jìn)行長(zhǎng)事件的處理,比如一些多請(qǐng)求的情況,就會(huì)妨礙主線程的執(zhí)行[阻塞]。這樣會(huì)妨礙主線程中被稱為RunLoop[在iOS和OS X的應(yīng)用中]的主循環(huán)的執(zhí)行,從而導(dǎo)致不能更新用戶界面,應(yīng)用程序的畫面長(zhǎng)時(shí)間停滯等問題。而使用了多線程編程,在執(zhí)行長(zhǎng)時(shí)間的處理仍可保證用戶界面的相應(yīng)性能。
圖6 多線程編程的優(yōu)點(diǎn)
那么我們iOS開發(fā)者,蘋果給我們提供了哪些多線程技術(shù)呢?
一.NSThread
我們先來介紹一下最古老但不怎么用的NSThread,下面是NSThread的所有API及注釋,可參考了解,詳細(xì)用法可以百度搜索詳細(xì)用法,在這里不多做論述了哈~壓軸的且看下文GCD。
+ (NSThread*)currentThread; //獲取當(dāng)前線程
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullableid)argument;//這個(gè)方法可以直接生成一個(gè)線程并啟動(dòng)它,而且無需為線程的清理負(fù)責(zé)
+ (BOOL)isMultiThreaded;// 判斷是否為多線程
@property(readonly,retain) NSMutableDictionary *threadDictionary;
+ (void)sleepUntilDate:(NSDate*)date;//設(shè)置線程阻塞時(shí)間
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;//設(shè)置線程阻塞時(shí)間
+ (void)exit;//退出
+ (double)threadPriority;//優(yōu)先級(jí)
+ (BOOL)setThreadPriority:(double)p;//優(yōu)先級(jí)
@property double threadPriority ? NS_AVAILABLE(10_6,4_0);// To be deprecated; use qualityOfService below
@property NSQualityOfServicequalityOfService ?NS_AVAILABLE(10_10,8_0);// read-only after the thread is started
+ (NSArray *)callStackReturnAddressesNS_AVAILABLE(10_5,2_0);// 線程函數(shù)地址
+ (NSArray *)callStackSymbols? NS_AVAILABLE(10_6,4_0);//查看方法被調(diào)用的callstack
@property (nullable,copy) NSString*name ?NS_AVAILABLE(10_5,2_0);//線程名字
@property NSUInteger stackSize NS_AVAILABLE(10_5,2_0);//堆棧大小
@property (readonly) BOOL isMainThreadNS_AVAILABLE(10_5,2_0);//是否是主線程
+ (BOOL)isMainThread ?NS_AVAILABLE(10_5,2_0);// reports whether current thread is main 是否為主線程
+ (NSThread*)mainThread ?NS_AVAILABLE(10_5,2_0);//獲取主線程
- (instancetype)init ?NS_AVAILABLE(10_5,2_0)NS_DESIGNATED_INITIALIZER;//初始化
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullableid)argumentNS_AVAILABLE(10_5,2_0);//初始化并且具有參數(shù)
@property(readonly,getter=isExecuting) BOOL executing NS_AVAILABLE(10_5,2_0);//線程是否正在執(zhí)行
@property(readonly,getter=isFinished) BOOL finished NS_AVAILABLE(10_5,2_0);//線程是否已經(jīng)結(jié)束
@property(readonly,getter=isCancelled) BOOL cancelled NS_AVAILABLE(10_5,2_0);//線程是否已經(jīng)取消
- (void)cancel NS_AVAILABLE(10_5,2_0);//線程取消
- (void)start NS_AVAILABLE(10_5,2_0);//線程啟動(dòng)
- (void)main NS_AVAILABLE(10_5,2_0);//線程啟動(dòng)
NSThread的優(yōu)缺點(diǎn):
優(yōu)點(diǎn):非常直觀,簡(jiǎn)單快捷。
缺點(diǎn):不能對(duì)線程進(jìn)行更加詳細(xì)的配置,所以一般開發(fā)中不推薦使用NSThread。
二.NSOperation
NSOperation是面向?qū)ο蟮亩嗑€程技術(shù),不過底層還是GCD實(shí)現(xiàn)的,效率比GCD要低一些。NSOperation有兩個(gè)子類NSInvocationOperation和NSBlockOperation并且我們還可以自定義子類集成NSOperation,配置一些自定義的方法。NSOperation要和NSOperationQueue一塊使用,才能發(fā)揮威力。
本篇的標(biāo)題已經(jīng)說明了,本文旨在說多線程和GCD,對(duì)NSOperation不做詳細(xì)論述,如果還想繼續(xù)了解詳細(xì)用法,請(qǐng)移步Y(jié)u大師的http://www.lxweimin.com/p/a044cd145a3d。
- (void)start; //啟動(dòng)
- (void)main;//啟動(dòng)
@property(readonly,getter=isCancelled) BOOL cancelled;//是否已經(jīng)取消
- (void)cancel;//取消
@property(readonly,getter=isExecuting) BOOL executing;//是否正在執(zhí)行
@property(readonly,getter=isFinished) BOOL finished;//是否已經(jīng)結(jié)束
@property(readonly,getter=isConcurrent) BOOL concurrent;// To be deprecated; use and override 'asynchronous' below//是否并發(fā)
@property(readonly,getter=isAsynchronous) BOO Lasynchronous NS_AVAILABLE(10_8,7_0);
@property(readonly,getter=isReady)BOOL ready;
- (void)addDependency:(NSOperation*)op;//添加依賴
- (void)removeDependency:(NSOperation*)op;//移除依賴
@property(readonly,copy)NSArray *dependencies;//獲取所有依賴
typedefNS_ENUM(NSInteger, NSOperationQueuePriority) {//隊(duì)列優(yōu)先級(jí)
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal =0,
NSOperationQueuePriorityHigh =4,
NSOperationQueuePriorityVeryHigh =8
};
@property NSOperationQueuePriority queuePriority;//優(yōu)先級(jí)屬性
@property(nullable,copy) void(^completionBlock)(void)NS_AVAILABLE(10_6,4_0);//執(zhí)行結(jié)束block
- (void)waitUntilFinished NS_AVAILABLE(10_6,4_0);
@property double threadPriority NS_DEPRECATED(10_6,10_10,4_0,8_0);
@property NSQualityOfService qualityOfServiceNS_AVAILABLE(10_10,8_0);
@property(nullable,copy) NSString *nameNS_AVAILABLE(10_10,8_0);
三.GCD
蘋果官方對(duì)GCD是這樣說明的:開發(fā)者要做的只是定義想執(zhí)行的任務(wù)并追加到適當(dāng)?shù)腄ispatch Queue中。Dispatch Queue是執(zhí)行處理的等待隊(duì)列,我們可以通過dispatch_async等API,在block語法中記述想要執(zhí)行的處理并將其追加到Dispatch Queue中,Dispatch Queue是按照追加的順序 進(jìn)行處理,先進(jìn)先出FIFO。
圖7 通過dispatch queue執(zhí)行處理
用代碼表示就是:
dispatch_async(queue,^{
//想執(zhí)行的任務(wù),這樣執(zhí)行就是在另一個(gè)新開辟的線程中
});
在執(zhí)行處理時(shí)候是有兩種Dispatch Queue,一種是Serial Dispatch Queue串行調(diào)度隊(duì)列,這個(gè)是等待現(xiàn)在執(zhí)行中的事件處理結(jié)束,另一種是Concurrent Dispatch Queue并發(fā)調(diào)度隊(duì)列,這個(gè)是不等待現(xiàn)在執(zhí)行中的事件處理結(jié)束。
圖8 Serial Dispatch Queue
那我們來舉個(gè)???? 來區(qū)分一下兩種調(diào)度隊(duì)列的區(qū)別:
有ABC三個(gè)任務(wù)等待執(zhí)行,
dispatch_async(queue,A)
dispatch_async(queue,B)
dispatch_async(queue,C)
如果queue使用Serial Dispatch Queue,則同時(shí)執(zhí)行的處理數(shù)只有一個(gè),A執(zhí)行結(jié)束才能執(zhí)行B,B執(zhí)行結(jié)束才能執(zhí)行C,A->B->C順序執(zhí)行。
如果queue使用Concurrent Dispatch Queue,這樣不用等待現(xiàn)在執(zhí)行中的處理結(jié)束,可以并行執(zhí)行多個(gè)處理,但并行執(zhí)行的處理數(shù)取決于iOS和OS X的CPU核數(shù)以及CPU負(fù)荷等當(dāng)前系統(tǒng)狀態(tài)。所謂“并發(fā)執(zhí)行”,就是使用多個(gè)線程同時(shí)執(zhí)行多個(gè)處理。
圖10 Serial開辟線程數(shù)量
圖11 Concurrent開辟線程數(shù)量
現(xiàn)在各位看官應(yīng)該可以明白Serial Dispatch Queue和Concurrent Dispatch Queue的大致區(qū)別了,那么我們?cè)鯓硬拍艿玫竭@些Dispatch Queue呢?一共分為兩種。
第一種:通過GCD的API的dispatch_queue_create函數(shù)生成Dispatch Queue
dispatch_queue_create
生成Serial Dispatch Queue代碼:
dispatch_queue_t? exampleSerialDispatchQueue = dispatch_queue_create("exampleSerialDispatchQueue.gcd.example.com",NULL)
我們先講一下Serial Dispatch Queue生成個(gè)數(shù)的問題,上面也講到了Serial Dispatch Queue只會(huì)生成一個(gè)線程,同一時(shí)間執(zhí)行一個(gè)處理,如果想要實(shí)現(xiàn)Concurrent Dispatch Queue并發(fā)執(zhí)行的效果,我們可以將多個(gè)任務(wù)放到多個(gè)Serial Dispatch Queue。
圖12 多個(gè)Serial Dispatch Queue
但是如果有N個(gè)的任務(wù),就需要?jiǎng)?chuàng)建N個(gè)線程,就會(huì)消耗大量?jī)?nèi)存,降低系統(tǒng)的響應(yīng)性能。所以當(dāng)需要遇到這種需求還是首先使用Concurrent Dispatch Queue。
接下來要講解一下這個(gè)函數(shù)的組成,dispatch_queue_create的第一個(gè)參數(shù)指定了Dispatch Queue的名稱,推薦使用name,雖然使用NULL也可以,但是為了后期調(diào)試維護(hù)還是給定一個(gè)name。第二個(gè)參數(shù)為NULL,則生成Serial Dispatch Queue。如果要生成Concurrent Dispatch Queue時(shí),第二個(gè)參數(shù)要使用DISPATCH_QUEUE_CONCURRENT。
dispatch_queue_t? exampleConcurrentDispatchQueue = dispatch_queue_create("exampleSerialDispatchQueue.gcd.example.com",DISPATCH_QUEUE_CONCURRENT),
另外需要注意的點(diǎn)是:雖然有ARC編譯器自動(dòng)管理內(nèi)存這一優(yōu)秀技術(shù),但生成的Dispatch Queue必須由程序員主動(dòng)釋放。
dispatch_release(exampleSerialDispatchQueue) 釋放
dispatch_retain(exampleSerialDispatchQueue) 持有
第二種:直接使用系統(tǒng)提供的標(biāo)準(zhǔn)Dispatch Queue
Main Dispatch Queue和Global Dispatch Queue
Main Dispatch Queue是在主線程中執(zhí)行的Dispatch Queue,也就是Serial Dispatch Queue。
圖13 Main Dispatch Queue
Global Dispatch Queue不需要通過dispatch_queue_create函數(shù)來逐個(gè)生成Concurrent Dispatch Queue,只要獲取Global Dispatch Queue就行了。Global Dispatch Queue的執(zhí)行優(yōu)先級(jí)有四種:
Global Dispatch Queue(High Priority) Global Dispatch Queue ?優(yōu)先級(jí)高
Global Dispatch Queue(Default Priority) Global Dispatch Queue? 優(yōu)先級(jí)默認(rèn)
Global Dispatch Queue(Low Priority) Global Dispatch Queue? 優(yōu)先級(jí)低
Global Dispatch Queue(Backgroud Priority) Global Dispatch Queue ?優(yōu)先級(jí)后臺(tái)
附上:Main Dispatch Queue的說明
Main Dispatch Queue? Serial Dispatch Queue ? 主線程執(zhí)行
Main Dispatch Queue的獲取方法:
dispatch_queue_t ? mainDispatchQueue = dispath_get_main_queue();
Global Dispatch Queue的獲取方法:
dispatch_queue_t globalDispatchQueueHigh = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0) ?//高優(yōu)先級(jí)
dispatch_queue_t globalDispatchQueueDefault = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)? //默認(rèn)優(yōu)先級(jí)
dispatch_queue_t globalDispatchQueueLow = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0)? //低優(yōu)先級(jí)
dispatch_queue_t globalDispatchQueueBackgroud = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_GACKGROUND,0)? //高優(yōu)先級(jí)
下面舉出一個(gè)簡(jiǎn)單的示例代碼:
//在默認(rèn)優(yōu)先級(jí)的Global Dispatch Queue中執(zhí)行block
dispatch_async(dispatch_get_global_queue,0),^{
//并行想要執(zhí)行的代碼
//在Main Dispatch Queue中執(zhí)行block
dispatch_async(dispatch_get_main_queue(),^{
//想要在主線程中執(zhí)行的代碼 如刷新UI
});
});
dispatch_after
此函數(shù)是可以實(shí)現(xiàn)一些延遲處理的功能,比如產(chǎn)品汪想要點(diǎn)擊一個(gè)按鈕,延遲兩秒鐘再執(zhí)行操作這個(gè)場(chǎng)景。
dispatch_time_t time = dispatch_time(DISPATCH_TIME_ROW, 3ull * NSEC_PER_SEC);
dispatch_after (time ,dispatch_get_main_queue(),^{
//等待三秒之后要執(zhí)行的操作
});
"ull"是C語言的數(shù)值字面量,是顯示表明類型時(shí)使用的字符串(表示“unsigned long long”),如果NSEC_PER_SEC替換為NSEC_PER_MSEC則是以毫秒為單位進(jìn)行計(jì)算。
DISPATCH_TIME_NOW表示現(xiàn)在的時(shí)間,到這里,需要注意一下,dispatch_after函數(shù)并不是在指定時(shí)間后執(zhí)行處理,而是在指定時(shí)間追加處理到Dispatch Queue。
dispatch group
有時(shí)候我們會(huì)有這種需求,在剛進(jìn)去一個(gè)頁面需要發(fā)送兩個(gè)請(qǐng)求,并且某種特定操作必須在兩個(gè)請(qǐng)求都結(jié)束(成功或失敗)的時(shí)候才會(huì)執(zhí)行,最low的辦法第二個(gè)請(qǐng)求嵌套在第一個(gè)請(qǐng)求結(jié)果后在發(fā)送,在第二個(gè)請(qǐng)求結(jié)束后再執(zhí)行操作。還有就是只使用一個(gè)Serial Dispatch Queue,把想要執(zhí)行的操作全部追加到這個(gè)Serial Dispatch Queue中并在最后追加某種特定操作,頗為復(fù)雜操作。但是呢,我們這里介紹更高級(jí)的辦法使用dispatch group。
我們將ABC三個(gè)任務(wù)block追加到Global Dispatch Queue,ABC全部執(zhí)行完,會(huì)執(zhí)行dispatch_group_notify中的block。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_t ?group = dispatch_group_creat();
dispatch_group_async(group,queue,^{執(zhí)行任務(wù)A});
dispatch_group_async(group,queue,^{執(zhí)行任務(wù)B});
dispatch_group_async(group,queue,^{執(zhí)行任務(wù)C});
dispatch_group_notify(group,dispatch_get_main_queue(),^{執(zhí)行最終的特定操作});
ABC的執(zhí)行順序不固定,因?yàn)槭荊lobal Dispatch Queue即Concurrent Dispatch Queue多個(gè)現(xiàn)場(chǎng)并行執(zhí)行。
上面的dispatch_group_notify(group,dispatch_get_main_queue(),^{執(zhí)行最終的特定操作});操作還可以更改為
dispatch_group_wait(group,DISPATCH_TIME_FOREVER);
dispatch_group_wait第二個(gè)參數(shù)指定為等待的時(shí)間(超時(shí)),屬于dispatch_time_t類型,在這里使用DISPATCH_TIME_FOREVER,意味著永久等待。如果dispatch group的處理尚未結(jié)束,就會(huì)一直等待。
如果指定等待時(shí)間為1秒如下:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NESC_PER_SEC);
long result = dispatch_group_wait(group,time);
if(result == 0) { dispatch group的全部處理執(zhí)行結(jié)束}
else { dispatch groupe的某一處理還在執(zhí)行中};
但是呢上面這種dispatch_group的排列執(zhí)行方式,是不會(huì)考慮block塊內(nèi)部的異步請(qǐng)求情況的,它只能保證把block內(nèi)的非異步直觀代碼執(zhí)行完,所以如果ABC三個(gè)任務(wù)中如果有執(zhí)行異步的請(qǐng)求,那么在dispatch_group_notify最終任務(wù)執(zhí)行中,那個(gè)異步請(qǐng)求不一定毀掉結(jié)束。
在這里給大家介紹針對(duì)這種問題另一個(gè)API。
dispatch_group_enter/dispatch_group_leave
dispatch_group_t group = disoatch_group_creat();
dispatch_group_enter(group);
dispatch_async(dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
//在這里執(zhí)行異步請(qǐng)求A
并且在執(zhí)行結(jié)束代碼(成功或失敗)中寫上dispatch_group_leave(group);
});
dispatch_async(dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
//在這里執(zhí)行異步請(qǐng)求B
并且在執(zhí)行結(jié)束代碼(成功或失敗)中寫上dispatch_group_leave(group);
});
dispatch_group_notify(group,dispatch_get_main_queue(),^{執(zhí)行最終的特定操作});
上面這種做法當(dāng)執(zhí)行到dispatch_group_notify,一定是AB兩個(gè)異步請(qǐng)求都加在結(jié)束了。dispatch_group_enter(group)和dispatch_group_leave(group);必須成對(duì)出現(xiàn),編譯器會(huì)強(qiáng)制識(shí)別當(dāng)出現(xiàn)dispatch_group_leave全部結(jié)束才執(zhí)行dispatch_group_notify,即使這種,檔異步執(zhí)行了AB兩個(gè)請(qǐng)求,也不能保證請(qǐng)求執(zhí)行結(jié)束的先后順序,如果任務(wù)B的請(qǐng)求參數(shù)包含請(qǐng)求A的返回參數(shù),那么只能用最Low的辦法將請(qǐng)求B嵌套在A中執(zhí)行,當(dāng)然如果你有更好的辦法歡迎提出來哈。
dispatch_apply
這個(gè)函數(shù)可以給定指定的次數(shù)將block追加到指定的Dispatch Queue中,并且等待全部結(jié)束處理執(zhí)行。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_apply(10,queue,^(size_t index){
NSLog(@“%zu”,idnex);
});
NSLog(@“done”);
執(zhí)行結(jié)果:4,1,0,3,5,2,6,8,9,7,done
第一個(gè)參數(shù)是重復(fù)次數(shù),第二個(gè)參數(shù)是追加對(duì)象的dispatch queue,第三個(gè)參數(shù)是追加的處理。dispatch_apply可以做遍歷數(shù)組的操作,不必一個(gè)一個(gè)寫for循環(huán)。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_apply([array count],queue,^(size_t index)){
NSLog(@"%zu:%@",index,[array objectAtIndex:index]);
}
dispatch_suspend/dispatch_resume
如果有時(shí)候你希望不執(zhí)行已經(jīng)追加到dispatch queue中的一些處理,比如這些處理會(huì)對(duì)驗(yàn)算結(jié)果造成影響。在這種情況,只需要掛起dispatch_queue即可,需要時(shí)再恢復(fù)。
dispatch_suspend函數(shù)掛起指定的dispatch_queue:
dispatch_suspend(queue);
dispatch_suspend函數(shù)恢復(fù)指定的dispatch_queue:
dispatch_resume(queue);
函數(shù)對(duì)已經(jīng)執(zhí)行的處理沒有影響,掛起后,追加到dispatch queue中但尚未執(zhí)行的處理在此之后停止執(zhí)行。而恢復(fù)則使得這些處理能夠繼續(xù)執(zhí)行。
dispatch_once
這個(gè)函數(shù)保證在應(yīng)用程序執(zhí)行中只執(zhí)行一次指定處理的API。下面先舉一個(gè)不用dispatch_once的常見的寫法:一個(gè)對(duì)象A
if(!對(duì)象A){
對(duì)象A =[ [對(duì)象A? alloc] init];
}
使用dispatch_once函數(shù)為:
static dispatch_once_ ?onceToken;
dispatch_once( &onceToken,^{
對(duì)象A =[ [對(duì)象A? alloc] init];
});
通過dispatch_once創(chuàng)建的即使在多線程環(huán)境下執(zhí)行也百分百安全。
另外關(guān)于GCD的API還有諸如:dispatch_set_target_queue,dispatch_barrier_async, disapatch_sync,dispatch Semaphore等 在這里不多做說明,不常用到用到的話可以查官方API。
有人覺得如果努力編寫線程管理的代碼,根本不需要GCD,但是呢,無論編程人員如何編寫管理線程的代碼,在性能方面也不可能勝過XNU內(nèi)核級(jí)所實(shí)現(xiàn)的GCD。我們盡量多使用GCD或者使用了Cocoa框架GCD的NSOperationQueue類等API。