iOS-GCD筆記

GCD筆記

總結(jié)一下多線程部分,最強(qiáng)大的無疑是GCD,那么先從這一塊部分講起.

Dispatch Queue的種類

GCD 提供有 dispatch queues 來處理代碼塊,這些隊(duì)列管理你提供給 GCD 的任務(wù)并用 FIFO 順序執(zhí)行這些任務(wù)。這就保證了第一個(gè)被添加到隊(duì)列里的任務(wù)會(huì)是隊(duì)列中第一個(gè)開始的任務(wù)餐禁,而第二個(gè)被添加的任務(wù)將第二個(gè)開始踢代,如此直到隊(duì)列的終點(diǎn)命辖。

所有的調(diào)度隊(duì)列(dispatch queues)自身都是線程安全的胎撤,你能從多個(gè)線程并行的訪問它們创坞。當(dāng)你了解了調(diào)度隊(duì)列如何為你自己代碼的不同部分提供線程安全后平酿,GCD的優(yōu)點(diǎn)就是顯而易見的凤优。關(guān)于這一點(diǎn)的關(guān)鍵是選擇正確類型的調(diào)度隊(duì)列和正確的調(diào)度函數(shù)來提交你的工作。

在本節(jié)你會(huì)看到兩種調(diào)度隊(duì)列蜈彼,都是由 GCD 提供的

  • Serial Dispatch Queue 等待現(xiàn)在執(zhí)行中處理結(jié)束,也就是我們常說的串行隊(duì)列 串行隊(duì)列中的任務(wù)一次執(zhí)行一個(gè)筑辨,每個(gè)任務(wù)只在前一個(gè)任務(wù)完成時(shí)才開始。而且柳刮,你不知道在一個(gè) Block 結(jié)束和下一個(gè)開始之間的時(shí)間長度.這些任務(wù)的執(zhí)行時(shí)機(jī)受到 GCD 的控制挖垛;唯一能確保的事情是 GCD 一次只執(zhí)行一個(gè)任務(wù)痒钝,并且按照我們添加到隊(duì)列的順序來執(zhí)行。
  • Concurrent Dispatch Queue 不等待現(xiàn)在執(zhí)行中處理結(jié)束,也就是我們常說的并行隊(duì)列.在并發(fā)隊(duì)列中的任務(wù)能得到的保證是它們會(huì)按照被添加的順序開始執(zhí)行痢毒,但這就是全部的保證了送矩。任務(wù)可能以任意順序完成,你不會(huì)知道何時(shí)開始運(yùn)行下一個(gè)任務(wù)哪替,或者任意時(shí)刻有多少 Block 在運(yùn)行栋荸。再說一遍,這完全取決于 GCD 凭舶。

GCD眾多API解析

1.Dispatch_queue_creat

  • 生成一個(gè)串行隊(duì)列,按照順序執(zhí)行隊(duì)列里的任務(wù)
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_creat("com.example.gcd.MyConcurrentDispatchQueue",NULL);
  • 生成一個(gè)并行隊(duì)列,同時(shí)執(zhí)行隊(duì)列里的任務(wù)
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_creat("com.example.gcd.MyConcurrentDispatchQueue",DISPATCH_QUEUE_CONCURRENT);

注意:非ARC情況下,通過creat函數(shù)生成的隊(duì)列,必須程序員自己來進(jìn)行內(nèi)存管理,通過dispatch_release()或者dispatch_retain()來進(jìn)行內(nèi)存管理,ARC情況下,無須我們自己處理

2.Main Dispatch Queue/Global Dispatch Queue

Global Dispatch Queue 有4個(gè)優(yōu)先級(jí),分別是高優(yōu)先級(jí)(High Priority) \默認(rèn)優(yōu)先級(jí)(Default Priority)\低優(yōu)先級(jí)(Low Priority)和后臺(tái)優(yōu)先級(jí)(Background Priority).

首先晌块,系統(tǒng)提供給你一個(gè)叫做 主隊(duì)列(main queue) 的特殊隊(duì)列。和其它串行隊(duì)列一樣帅霜,這個(gè)隊(duì)列中的任務(wù)一次只能執(zhí)行一個(gè)匆背。然而,它能保證所有的任務(wù)都在主線程執(zhí)行身冀,而主線程是唯一可用于更新 UI 的線程钝尸。這個(gè)隊(duì)列就是用于發(fā)生消息給 UIView 或發(fā)送通知的。

系統(tǒng)同時(shí)提供給你好幾個(gè)并發(fā)隊(duì)列搂根。它們叫做 全局調(diào)度隊(duì)列(Global Dispatch Queues) 珍促。目前的四個(gè)全局隊(duì)列有著不同的優(yōu)先級(jí):background、low剩愧、default 以及 high默辨。要知道吴菠,Apple 的 API 也會(huì)使用這些隊(duì)列隔嫡,所以你添加的任何任務(wù)都不會(huì)是這些隊(duì)列中唯一的任務(wù)捂敌。

最后难咕,你也可以創(chuàng)建自己的串行隊(duì)列或并發(fā)隊(duì)列坤检。這就是說匀奏,至少有五個(gè)隊(duì)列任你處置:主隊(duì)列蛔翅、四個(gè)全局調(diào)度隊(duì)列,再加上任何你自己創(chuàng)建的隊(duì)列充包。
以上是調(diào)度隊(duì)列的大框架!
GCD 的“藝術(shù)”歸結(jié)為選擇合適的隊(duì)列來調(diào)度函數(shù)以提交你的工作,下面介紹一下系統(tǒng)提供的眾多函數(shù)

3.Dispatch_set_target_queue

功能:變更生成的Dispatch Queue 的執(zhí)行優(yōu)先級(jí)要使用該函數(shù).

4.Dispatch_after

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ <#code to be executed after a specified delay#> });

注意:dispatch_after函數(shù)并不是在指定時(shí)間后執(zhí)行處理,而是在指定時(shí)間追加處理到Dispatch Queue. dispatch_after 工作起來就像一個(gè)延遲版的 dispatch_async遥椿。你依然不能控制實(shí)際的執(zhí)行時(shí)間基矮,且一旦 dispatch_after返回也就不能再取消它。

  • 第一個(gè)參數(shù)是指定時(shí)間,用的dispatch_time_t類型的值.該值使用dispatch_time函數(shù)或dispatch_walltime函數(shù)作成.
    dispatch_time用于計(jì)算相對(duì)時(shí)間
    dispatch_walltime用于計(jì)算絕對(duì)時(shí)間
    ull代表unsigned long long
    數(shù)值和NSEC_PER_SEC的乘積得到單位為秒的數(shù)值
    數(shù)值和NSEC_PER_MSEC的乘積得到單位為毫秒的數(shù)值DISPATCH_TIME_NOW表示現(xiàn)在的時(shí)間關(guān)于dispatch_time冠场,第一個(gè)參數(shù)通常使用DISPATCH_TIME_NOW家浇,它是一個(gè)表示dispatch_time_t 的宏,表示從現(xiàn)在開始算起
  • 第二個(gè)參數(shù)是第一個(gè)參數(shù)之后經(jīng)歷的時(shí)長碴裙。而我們通常用的NSEC_PER_SEC也是一個(gè)宏钢悲,還有其他的宏:
#define NSEC_PER_SEC 1000000000ull //每秒有多少納秒
#define NSEC_PER_MSEC 1000000ull //每毫秒有多少納秒
#define USEC_PER_SEC 1000000ull //每秒有多少微秒
#define NSEC_PER_USEC 1000ull //每微秒有多少納秒

因?yàn)?秒鐘有NSEC_PER_SEC(也即1000000000)納秒点额,所以NSEC_PER_SEC其實(shí)就相當(dāng)于1秒,那么兩秒就是2 *NSEC_PER_SEC莺琳;
同理还棱,NSEC_PER_MSEC就相當(dāng)于是1毫秒,那么2秒鐘惭等,就應(yīng)該是2000 * NSEC_PER_MSEC珍手;
USEC_PER_SEC也相當(dāng)于1毫秒,2秒鐘就是2000 *USEC_PER_SEC
NSEC_PER_USEC相當(dāng)于 1微妙辞做,所以要表示1秒鐘就是1000000 NSEC_PER_USEC琳要。
所以1秒后執(zhí)行某段代碼可以這樣寫:

 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"哈哈"); }); 
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1000 * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{ NSLog(@"嘿嘿"); }); 
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1000 * USEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"嗯嗯"); }); 
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1000000 * NSEC_PER_USEC)), dispatch_get_main_queue(), ^{ NSLog(@"呃呃"); });

這個(gè)API的作用與下面這個(gè)方法類似:

[self performSelector:@selector(testClick:) withObject:nil afterDelay:2.0];

不知道何時(shí)適合使用 dispatch_after ?

  • 自定義串行隊(duì)列:在一個(gè)自定義串行隊(duì)列上使用 dispatch_after 要小心秤茅。你最好堅(jiān)持使用主隊(duì)列稚补。
  • 主隊(duì)列(串行):是使用 dispatch_after 的好選擇;Xcode 提供了一個(gè)不錯(cuò)的自動(dòng)完成模版框喳。
  • 并發(fā)隊(duì)列:在并發(fā)隊(duì)列上使用 dispatch_after 也要小心孔厉;你會(huì)這樣做就比較罕見。還是在主隊(duì)列做這些操作吧帖努。

5.Dispatch Group

使用場景:在多個(gè)異步任務(wù)全部執(zhí)行完畢后撰豺,執(zhí)行某個(gè)任務(wù)。如果用同步任務(wù)或串行隊(duì)列拼余,就沒有意義了污桦,要謹(jǐn)記。

這里有兩種實(shí)現(xiàn)方式:
方式一 利用dispatch_group_asyncdispatch_group_notify配合.
方式二 利用dispatch_group_enter 匙监、dispatch_group_leave
dispatch_group_notify配合凡橱,其中需要注意的是有dispatch_group_enter就必定有一個(gè)dispatch_group_leave與之對(duì)應(yīng),否則可能會(huì)出現(xiàn)令你意想不到的崩潰亭姥。

Dispatch Group 會(huì)在整個(gè)組的任務(wù)都完成時(shí)通知你稼钩。這些任務(wù)可以是同步的,也可以是異步的达罗,即便在不同的隊(duì)列也行坝撑。而且在整個(gè)組的任務(wù)都完成時(shí),Dispatch Group 可以用同步的或者異步的方式通知你粮揉。因?yàn)橐O(jiān)控的任務(wù)在不同隊(duì)列巡李,那就用一個(gè) dispatch_group_t 的實(shí)例來記下這些不同的任務(wù)。

當(dāng)組中所有的事件都完成時(shí)扶认,GCD 的 API 提供了兩種通知方式侨拦。

第一種是 dispatch_group_wait ,它會(huì)阻塞當(dāng)前線程辐宾,直到組里面所有的任務(wù)都完成或者等到某個(gè)超時(shí)發(fā)生狱从。這恰好是你目前所需要的膨蛮。

在我們轉(zhuǎn)向另外一種使用 Dispatch Group 的方式之前,先看一個(gè)簡要的概述季研,關(guān)于何時(shí)以及怎樣使用有著不同的隊(duì)列類型的 Dispatch Group :

  • 自定義串行隊(duì)列:它很適合當(dāng)一組任務(wù)完成時(shí)發(fā)出通知敞葛。
  • 主隊(duì)列(串行):它也很適合這樣的情況。但如果你要同步地等待所有工作地完成训貌,那你就不應(yīng)該使用它制肮,因?yàn)槟悴荒茏枞骶€程。然而递沪,異步模型是一個(gè)很有吸引力的能用于在幾個(gè)較長任務(wù)(例如網(wǎng)絡(luò)調(diào)用)完成后更新 UI 的方式豺鼻。
  • 并發(fā)隊(duì)列:它也很適合 Dispatch Group 和完成時(shí)通知。

dispatch_group_notify 以異步的方式工作款慨。當(dāng) Dispatch Group 中沒有任何任務(wù)時(shí)儒飒,它就會(huì)執(zhí)行其代碼,那么 completionBlock 便會(huì)運(yùn)行檩奠。你還指定了運(yùn)行 completionBlock 的隊(duì)列桩了,此處,主隊(duì)列就是你所需要的埠戳。

Dispatch_group_wait該API依然是與dispatch_group配合使用井誉。它會(huì)阻塞當(dāng)前所在的線程,直到前面的blocks 執(zhí)行完成整胃,或者超時(shí)的時(shí)候返回颗圣。該方法會(huì)同步的等待之前比較的block 對(duì)象們完成,如果在給定的時(shí)間內(nèi)沒有完成屁使,該方法就會(huì)返回在岂。如果在給定的時(shí)間超時(shí)前完成,則返回0,否則就返回一個(gè)非零的值蛮寂。

示例

dispatch_group_t group = dispatch_group_create(); 
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 5; i++) 
{
   dispatch_group_async(group, queue, ^{ 
      [NSThread sleepForTimeInterval:i];
     NSLog(@"并發(fā)%d結(jié)束----線程:%@", i,[NSThread currentThread]);
   }); 
} 
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC));
long result = dispatch_group_wait(group, time); 
if (result) { 
    NSLog(@"超時(shí)了");
 } else { 
    NSLog(@"執(zhí)行完畢");
}// 打印結(jié)果是:
2016-07-12 14:04:17.980 PractiseProject[4537:155453] 并發(fā)0結(jié)束----線程:<NSThread: 0x7fbbe8e115e0>{number = 3, name = (null)}
2016-07-12 14:04:18.981 PractiseProject[4537:155441] 并發(fā)1結(jié)束----線程:<NSThread: 0x7fbbe8f5ee60>{number = 4, name = (null)}
2016-07-12 14:04:19.980 PractiseProject[4537:155338] 超時(shí)了
2016-07-12 14:04:19.981 PractiseProject[4537:155456] 并發(fā)2結(jié)束----線程:<NSThread: 0x7fbbe8f5d260>{number = 5, name = (null)}
2016-07-12 14:04:20.985 PractiseProject[4537:155464] 并發(fā)3結(jié)束----線程:<NSThread: 0x7fbbe8f60e90>{number = 6, name = (null)}
2016-07-12 14:04:21.984 PractiseProject[4537:155453] 并發(fā)4結(jié)束----線程:<NSThread: 0x7fbbe8e115e0>{number = 3, name = (null)}

由于dispatch_group_wait會(huì)阻塞線程蔽午,在dispatch_group_wait后面的代碼并不會(huì)執(zhí)行,如果我們?cè)谥骶€程中執(zhí)行上面的代碼段酬蹋,則會(huì)阻塞UI界面及老。所以我們應(yīng)該在子線程中執(zhí)行上面的代碼片段(用一個(gè)dispatch_async包起來)。如果我們?cè)O(shè)置wait的時(shí)間為永遠(yuǎn)的話除嘹,由于在子線程中執(zhí)行的任務(wù)總有結(jié)束的時(shí)候写半,那么dispatch_group_wait之后執(zhí)行的代碼就效果就與上一篇中的dispatch_group_notify的功能類似啦。

示例:

dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
dispatch_async(queue, ^{ 
    dispatch_group_t group = dispatch_group_create(); 
    for (int i = 0; i < 5; i++) { 
        dispatch_group_async(group, queue, ^{ 
          [NSThread sleepForTimeInterval:i]; 
          NSLog(@"并發(fā)%d結(jié)束----線程:%@", i,[NSThread currentThread]); 
        } ); 
    }
    long result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 
    if (result) { 
        NSLog(@"超時(shí)了"); 
    } else { 
        dispatch_async(dispatch_get_main_queue(), ^{ 
            NSLog(@"執(zhí)行完畢,回主線程更新UI"); 
        });
    }
});

6.Dispatch_barrier_async和Dispatch_barrier_sync

這個(gè)函數(shù)的意思是:前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行,而且它后面的任務(wù)等它執(zhí)行完畢之后才能執(zhí)行.

該函數(shù)同dispatch_queue_creat函數(shù)生成的Concurrent Dispatch Queue一起使用.

注意:
1.從Xcode 4開始尉咕,我們定義property后,編譯器會(huì)自動(dòng)幫我們添加@synthesize璃岳,但是如果我們同時(shí)重寫setter和getter,那么編譯器便不再幫我們添加@synthesize年缎,我們需要自己添加@synthesize悔捶。
2.dispatch_barrier_async只能使用dispatch_queue_create創(chuàng)建的并發(fā)隊(duì)列,才能正確發(fā)揮它的作用单芜。

dispatch_barrier_syncdispatch_barrier_async的功能基本一致蜕该,不同之處是,dispatch_barrier_sync是在當(dāng)前線程中執(zhí)行block中的任務(wù)洲鸠,而dispatch_barrier_async則是在新的線程(有可能是之前使用過的子線程)中執(zhí)行任務(wù)堂淡。 它們都是在用dispatch_queue_create創(chuàng)建的并發(fā)隊(duì)列上有效果,而在串行隊(duì)列或者dispatch_get_global_queue創(chuàng)建的并發(fā)隊(duì)列中扒腕,作用與dispatch_sync一致绢淀。dispatch_barrier決定的只是它的任務(wù)是否在新的線程中執(zhí)行,以及它一定在前面幾個(gè)任務(wù)執(zhí)行完后執(zhí)行瘾腰,并不會(huì)影響之前任務(wù)的執(zhí)行順序等皆的。在串行隊(duì)列或者dispatch_get_global_queue創(chuàng)建的并發(fā)隊(duì)列中,dispatch_barrier_sync僅僅相當(dāng)于dispatch_sync

dispatch_barrier_async函數(shù)會(huì)等待追加到Concurrent Dispatch Queue上的并行執(zhí)行的處理全部結(jié)束之后,再將指定的處理追加到該Concurrent Dispatch Queue中.然后在由dispatch_barrier_async函數(shù)追加的處理執(zhí)行完畢后,Concurrent Dispatch Queue才恢復(fù)一般的動(dòng)作,追加到該Concurrent Dispatch Queue的處理又開始并行執(zhí)行.
下面是你何時(shí)會(huì)——和不會(huì)——使用障礙函數(shù)的情況:

  • 自定義串行隊(duì)列:一個(gè)很壞的選擇蹋盆;障礙不會(huì)有任何幫助费薄,因?yàn)椴还茉鯓樱粋€(gè)串行隊(duì)列一次都只執(zhí)行一個(gè)操作栖雾。
  • 全局并發(fā)隊(duì)列:要小心楞抡;這可能不是最好的主意,因?yàn)槠渌到y(tǒng)可能在使用隊(duì)列而且你不能壟斷它們只為你自己的目的析藕。
  • 自定義并發(fā)隊(duì)列:這對(duì)于原子或臨界區(qū)代碼來說是極佳的選擇召廷。任何你在設(shè)置或?qū)嵗男枰€程安全的事物都是使用障礙的最佳候選。

由于上面唯一像樣的選擇是自定義并發(fā)隊(duì)列噪径,你將創(chuàng)建一個(gè)你自己的隊(duì)列去處理你的障礙函數(shù)并分開讀和寫函數(shù)柱恤。且這個(gè)并發(fā)隊(duì)列將允許多個(gè)多操作同時(shí)進(jìn)行。

7.Dispatch_sync同步函數(shù)和Dispatch_async異步函數(shù)

dispatch_sync添加任務(wù)到一個(gè)隊(duì)列并等待直到任務(wù)完成找爱。dispatch_async做類似的事情梗顺,但不同之處是它不會(huì)等待任務(wù)的完成,而是立即繼續(xù)“調(diào)用線程”的其它任務(wù)车摄。

特別注意:在同步函數(shù)執(zhí)行主線程隊(duì)列的任務(wù)會(huì)發(fā)生死鎖,特別注意同步函數(shù)的死鎖情況

下面是一個(gè)快速總覽寺谤,關(guān)于在何時(shí)以及何處使用 dispatch_sync :

  • 自定義串行隊(duì)列:在這個(gè)狀況下要非常小心!如果你正運(yùn)行在一個(gè)隊(duì)列并調(diào)用dispatch_sync 放在同一個(gè)隊(duì)列吮播,那你就百分百地創(chuàng)建了一個(gè)死鎖变屁。
  • 主隊(duì)列(串行):同上面的理由一樣,必須非常小心意狠!這個(gè)狀況同樣有潛在的導(dǎo)致死鎖的情況粟关。
  • 并發(fā)隊(duì)列:這才是做同步工作的好選擇,不論是通過調(diào)度障礙环戈,或者需要等待一個(gè)任務(wù)完成才能執(zhí)行進(jìn)一步處理的情況闷板。

下面是一個(gè)關(guān)于在 dispatch_async 上如何以及何時(shí)使用不同的隊(duì)列類型的快速指導(dǎo):

  • 自定義串行隊(duì)列:當(dāng)你想串行執(zhí)行后臺(tái)任務(wù)并追蹤它時(shí)就是一個(gè)好選擇澎灸。這消除了資源爭用,因?yàn)槟阒酪淮沃挥幸粋€(gè)任務(wù)在執(zhí)行遮晚。注意若你需要來自某個(gè)方法的數(shù)據(jù)性昭,你必須內(nèi)聯(lián)另一個(gè) Block 來找回它或考慮使用 dispatch_sync。
  • 主隊(duì)列(串行):這是在一個(gè)并發(fā)隊(duì)列上完成任務(wù)后更新 UI 的共同選擇县遣。要這樣做糜颠,你將在一個(gè) Block 內(nèi)部編寫另一個(gè) Block 。以及萧求,如果你在主隊(duì)列調(diào)用 dispatch_async 到主隊(duì)列其兴,你能確保這個(gè)新任務(wù)將在當(dāng)前方法完成后的某個(gè)時(shí)間執(zhí)行。
  • 并發(fā)隊(duì)列:這是在后臺(tái)執(zhí)行非 UI 工作的共同選擇饭聚。

| 函數(shù)\隊(duì)列 | 并發(fā)隊(duì)列 | 自己創(chuàng)建的串行隊(duì)列 | 主隊(duì)列 |
| 同步 | 不會(huì)創(chuàng)建新線程,串行執(zhí)行 | 不會(huì)創(chuàng)建新線程,串行執(zhí)行 |不會(huì)創(chuàng)建新線程,串行執(zhí)行(可能會(huì)發(fā)生死鎖,如果最外層函數(shù)也是在主線程執(zhí)行)|
| 異步 | 會(huì)創(chuàng)建多條新線程,同時(shí)執(zhí)行多個(gè)任務(wù) | 會(huì)創(chuàng)建新線程,串行執(zhí)行任務(wù) |不會(huì)創(chuàng)建新線程,串行執(zhí)行|


8.Dispatch_apply

dispatch_apply函數(shù)是dispatch_sync函數(shù)和Dispatch Group的關(guān)聯(lián)API.該函數(shù)按指定的次數(shù)將指定的Block追加到指定的Dispatch Queue中,并等待全部處理執(zhí)行結(jié)束.

dispatch_apply 表現(xiàn)得就像一個(gè) for 循環(huán)忌警,但它能并發(fā)地執(zhí)行不同的迭代。這個(gè)函數(shù)是同步的秒梳,所以和普通的 for 循環(huán)一樣法绵,它只會(huì)在所有工作都完成后才會(huì)返回。

那何時(shí)才適合用 dispatch_apply 呢酪碘?

  • 自定義串行隊(duì)列:串行隊(duì)列會(huì)完全抵消 dispatch_apply 的功能朋譬;你還不如直接使用普通的 for 循環(huán)。
  • 主隊(duì)列(串行):與上面一樣兴垦,在串行隊(duì)列上不適合使用 dispatch_apply 徙赢。還是用普通的 for 循環(huán)吧。
  • 并發(fā)隊(duì)列:對(duì)于并發(fā)循環(huán)來說是很好選擇探越,特別是當(dāng)你需要追蹤任務(wù)的進(jìn)度時(shí)狡赐。

該方法會(huì)等apply 中多次迭代調(diào)用的block全都執(zhí)行完成后,才會(huì)返回钦幔,所以dispatch_apply會(huì)阻塞當(dāng)前線程枕屉,我們得避免在主線程中使用dispatch_apply。另外鲤氢,說明中已經(jīng)說的很清楚了搀擂,如果我們使用dispatch_get_global_queue創(chuàng)建的串行隊(duì)列,那么傳入的block任務(wù)是并發(fā)執(zhí)行的卷玉。如果我們?cè)诖嘘?duì)列中執(zhí)行該方法哨颂,會(huì)發(fā)生死鎖,所以第二個(gè)參數(shù)相种,千萬不要傳串行隊(duì)列威恼。如果我們使用dispatch_queue_create創(chuàng)建的并發(fā)隊(duì)列,block任務(wù)依然是順序執(zhí)行的。

下面看一下示例代碼以及運(yùn)行結(jié)果:dispatch_apply / dispatch_queue_create /并發(fā)隊(duì)列:

dispatch_queue_t queue = dispatch_queue_create("com.haley.cn", DISPATCH_QUEUE_CONCURRENT); 
dispatch_async(queue, ^{ 
    dispatch_apply(5, queue, ^(size_t index) { 
        [NSThread sleepForTimeInterval:index];
        NSLog(@"并發(fā)%zu---%@",index,[NSThread currentThread]);  
     });
    NSLog(@"done - %@",[NSThread currentThread]); 
}); 
NSLog(@"主線程");
// 輸出結(jié)果:
2016-07-12 16:09:33.856 PractiseProject[5496:207665] 主線程
2016-07-12 16:09:33.857 PractiseProject[5496:207710] 并發(fā)0---<NSThread: 0x7f950053a920>{number = 3, name = (null)}
2016-07-12 16:09:34.860 PractiseProject[5496:207710] 并發(fā)1---<NSThread: 0x7f950053a920>{number = 3, name = (null)}
2016-07-12 16:09:36.864 PractiseProject[5496:207710] 并發(fā)2---<NSThread: 0x7f950053a920>{number = 3, name = (null)}
2016-07-12 16:09:39.867 PractiseProject[5496:207710] 并發(fā)3---<NSThread: 0x7f950053a920>{number = 3, name = (null)}
2016-07-12 16:09:43.872 PractiseProject[5496:207710] 并發(fā)4---<NSThread: 0x7f950053a920>{number = 3, name = (null)}
2016-07-12 16:09:43.873 PractiseProject[5496:207710] done - <NSThread: 0x7f950053a920>{number = 3, name = (null)}

dispatch_apply / dispatch_get_global_queue:

dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
dispatch_async(queue, ^{ 
    dispatch_apply(5, queue, ^(size_t index) { 
        [NSThread sleepForTimeInterval:index]; 
        NSLog(@"并發(fā)%zu---%@",index,[NSThread currentThread]); 
    }); 
    NSLog(@"done - %@",[NSThread currentThread]);
}); 
NSLog(@"主線程");
// 輸出結(jié)果:
2016-07-12 16:15:26.634 PractiseProject[5544:210845] 主線程
2016-07-12 16:15:26.634 PractiseProject[5544:210882] 并發(fā)0---<NSThread: 0x7f8733816bc0>{number = 3, name = (null)}
2016-07-12 16:15:27.637 PractiseProject[5544:210887] 并發(fā)1---<NSThread: 0x7f8731517e10>{number = 2, name = (null)}
2016-07-12 16:15:28.637 PractiseProject[5544:210893] 并發(fā)2---<NSThread: 0x7f8731412d50>{number = 4, name = (null)}
2016-07-12 16:15:29.636 PractiseProject[5544:210899] 并發(fā)3---<NSThread: 0x7f8731448cc0>{number = 5, name = (null)}
2016-07-12 16:15:30.635 PractiseProject[5544:210882] 并發(fā)4---<NSThread: 0x7f8733816bc0>{number = 3, name = (null)}
2016-07-12 16:15:30.635 PractiseProject[5544:210893] done - <NSThread: 0x7f8731412d50>{number = 4, name = (null)}

9.Dispatch_suspend(掛起)/Dispatch_resume(恢復(fù))

10.Dispatch Semaphore

dispatch_semaphore_t信號(hào)量是一種老式的線程概念沃测,由非常謙卑的 Edsger W. Dijkstra 介紹給世界缭黔。信號(hào)量之所以比較復(fù)雜是因?yàn)樗⒃诓僮飨到y(tǒng)的復(fù)雜性之上食茎。

- (void)downloadImageURLWithString:(NSString *)URLString
{ 
    // 1
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    NSURL *url = [NSURL URLWithString:URLString]; 
    __unused Photo *photo = [[Photo alloc] initwithURL:url withCompletionBlock:^(UIImage *image, NSError *error) { 
      if (error) { 
          XCTFail(@"%@ failed. %@", URLString, error);
      } 
     // 2 
     dispatch_semaphore_signal(semaphore); 
    }]; 
  // 3 
  dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, kDefaultTimeoutLengthInNanoSeconds);
  if (dispatch_semaphore_wait(semaphore, timeoutTime))  { 
    XCTFail(@"%@ timed out", URLString); 
  }
}

在我們使用多線程處理多個(gè)并發(fā)任務(wù)蒂破,而這多個(gè)并發(fā)任務(wù)有資源競爭的時(shí)候,就需要一種機(jī)制别渔,在資源不夠用時(shí)附迷,讓新的任務(wù)處于等待狀態(tài),當(dāng)有可用資源時(shí)哎媚,等待的任務(wù)在按序依次執(zhí)行喇伯。
像這一類問題除了可以用NSOperation,設(shè)置最大并發(fā)數(shù)外拨与,還可以使用信號(hào)量稻据。
這里涉及到的API有如下幾個(gè):
dispatch_semaphore_t dispatch_semaphore_create(long value);

創(chuàng)建信號(hào)量的方法,如果初始值小于0买喧,則會(huì)返回NULL捻悯,即信號(hào)量創(chuàng)建失敗。參數(shù):value 表示初始的信號(hào)量個(gè)數(shù)淤毛,可以理解為資源個(gè)數(shù)今缚,或者最大并發(fā)個(gè)數(shù)。

long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

參數(shù):第一個(gè)參數(shù)為信號(hào)量對(duì)象低淡,第二個(gè)參數(shù)是等待超時(shí)的時(shí)間姓言。
講解:該方法相當(dāng)于任務(wù)開始前的檢查,需要注意的是該方法會(huì)阻塞當(dāng)前線程蔗蹋。如果此時(shí)信號(hào)量的值大于0何荚,會(huì)返回0,并且代碼會(huì)繼續(xù)往下執(zhí)行猪杭;如果此時(shí)信號(hào)量的值等于0餐塘,那么此時(shí)該方法會(huì)阻塞當(dāng)前線程,等待timeout 的時(shí)間胁孙。如果在超時(shí)的時(shí)間內(nèi)唠倦,依然沒有可用的資源,那么該方法會(huì)返回一個(gè)非0的值涮较。

該方法執(zhí)行時(shí)稠鼻,會(huì)使信號(hào)量的值減1。

long dispatch_semaphore_signal(dispatch_semaphore_t dsema);

該方法應(yīng)該在任務(wù)執(zhí)行完畢時(shí)調(diào)用狂票,它會(huì)使信號(hào)量的值加0候齿。

下面用一段實(shí)際代碼演示GCD信號(hào)量的使用:

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 在子線程中執(zhí)行,防止阻塞主線程 
dispatch_async(queue, ^{ 
// 創(chuàng)建一個(gè)有3個(gè)資源的信號(hào)量 
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(3); 
    for (int i = 0; i < 6; i++) { 
        // 檢測還有多少個(gè)資源,執(zhí)行后會(huì)使資源數(shù)減少1       
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 
        dispatch_async(queue, ^{
           NSLog(@"開始執(zhí)行任務(wù)%d---%@",i,[NSThread currentThread]);
           [NSThread sleepForTimeInterval:6 - i]; 
           NSLog(@"完成任務(wù)%d---%@",i,[NSThread currentThread]); 
           //表示資源使用完畢慌盯,會(huì)使資源數(shù)加1     
           dispatch_semaphore_signal(semaphore);
     });
   }
});
// 輸出結(jié)果:
2016-07-13 17:23:53.178 PractiseProject[4973:196435] 開始執(zhí)行任務(wù)1---<NSThread: 0x7fc07060a090>{number = 4, name = (null)}
2016-07-13 17:23:53.178 PractiseProject[4973:196436] 開始執(zhí)行任務(wù)0---<NSThread: 0x7fc070722030>{number = 3, name = (null)}
2016-07-13 17:23:53.178 PractiseProject[4973:196437] 開始執(zhí)行任務(wù)2---<NSThread: 0x7fc070455f10>{number = 5, name = (null)}
2016-07-13 17:23:57.179 PractiseProject[4973:196437] 完成任務(wù)2---<NSThread: 0x7fc070455f10>{number = 5, name = (null)}
2016-07-13 17:23:57.179 PractiseProject[4973:196437] 開始執(zhí)行任務(wù)3---<NSThread: 0x7fc070455f10>{number = 5, name = (null)}
2016-07-13 17:23:58.182 PractiseProject[4973:196435] 完成任務(wù)1---<NSThread: 0x7fc07060a090>{number = 4, name = (null)}
2016-07-13 17:23:58.182 PractiseProject[4973:196435] 開始執(zhí)行任務(wù)4---<NSThread: 0x7fc07060a090>{number = 4, name = (null)}
2016-07-13 17:23:59.179 PractiseProject[4973:196436] 完成任務(wù)0---<NSThread: 0x7fc070722030>{number = 3, name = (null)}
2016-07-13 17:23:59.179 PractiseProject[4973:196436] 開始執(zhí)行任務(wù)5---<NSThread: 0x7fc070722030>{number = 3, name = (null)}
2016-07-13 17:24:00.184 PractiseProject[4973:196435] 完成任務(wù)4---<NSThread: 0x7fc07060a090>{number = 4, name = (null)}
2016-07-13 17:24:00.184 PractiseProject[4973:196437] 完成任務(wù)3---<NSThread: 0x7fc070455f10>{number = 5, name = (null)}
2016-07-13 17:24:00.184 PractiseProject[4973:196436] 完成任務(wù)5---<NSThread: 0x7fc070722030>{number = 3, name = (null)}

11.Dispatch_once

dispatch_once 函數(shù)是保證在應(yīng)用程序執(zhí)行中只執(zhí)行一次指定處理的API.
以線程安全的方式執(zhí)行且僅執(zhí)行其代碼塊一次周霉。試圖訪問臨界區(qū)(即傳遞給 dispatch_once 的代碼)的不同的線程會(huì)在臨界區(qū)已有一個(gè)線程的情況下被阻塞,直到臨界區(qū)完成為止亚皂。

12.Dispatch I/O

在讀取大文件時(shí),如果將文件分成合適的大小并使用Global Dispatch Queue并列讀取的話,就是使用dispatch I/ODispatch Data

13.Dispatch Source

GCD 的一個(gè)特別有趣的特性是 Dispatch Source俱箱,它基本上就是一個(gè)低級(jí)函數(shù)的 grab-bag ,能幫助你去響應(yīng)或監(jiān)測 Unix 信號(hào)灭必、文件描述符狞谱、Mach 端口、VFS 節(jié)點(diǎn)禁漓,以及其它晦澀的東西跟衅。所有這些都超出了本教程討論的范圍,但你可以通過實(shí)現(xiàn)一個(gè) Dispatch Source 對(duì)象并以一個(gè)相當(dāng)奇特的方式來使用它來品嘗那些晦澀的東西播歼。
有點(diǎn)不知道干嘛的,沒用過,可以看一下例子
GCD 深入理解:第二部分

14.Dispatch_source中的timer

dispatch_source_t 的類型有很多種:

#define DISPATCH_SOURCE_TYPE_DATA_ADD 
#define DISPATCH_SOURCE_TYPE_DATA_OR 
#define DISPATCH_SOURCE_TYPE_MACH_RECV 
#define DISPATCH_SOURCE_TYPE_MACH_SEND 
#define DISPATCH_SOURCE_TYPE_PROC 
#define DISPATCH_SOURCE_TYPE_READ 
#define DISPATCH_SOURCE_TYPE_SIGNAL 
#define DISPATCH_SOURCE_TYPE_TIMER 
#define DISPATCH_SOURCE_TYPE_VNODE 
#define DISPATCH_SOURCE_TYPE_WRITE 
#define DISPATCH_SOURCE_TYPE_MEMORYPRESSURE

這里記錄的是dispatch_source 中定時(shí)器timer的用法伶跷。
這里我就用dispatch_source 封裝一個(gè)timer 的方法,可以在傳入兩個(gè)block秘狞,分別在循環(huán)執(zhí)行叭莫,結(jié)束時(shí)執(zhí)行。當(dāng)然咯谒撼,下面這個(gè)方法還可以再加一個(gè)間隔時(shí)間參數(shù)食寡。

- (void)startTimerWithTimeout:(NSTimeInterval)timeout eventBlock:(void (^)())eventBlock endBlock:(void (^)())endBlock
{
     __block NSTimeInterval tempTimeout = timeout; 
    // 創(chuàng)建一個(gè)隊(duì)列,不管你創(chuàng)建什么類型的隊(duì)列,最終event_handler 都是在子線程中執(zhí)行的 
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
    // 創(chuàng)建一個(gè)計(jì)時(shí)器類型的源 
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); 
    // 設(shè)置定時(shí)器參數(shù)廓潜,定時(shí)器開始的時(shí)間抵皱、每隔多久執(zhí)行一次、精度(可以延遲的納秒數(shù)辩蛋,最高精度是0呻畸,實(shí)際還是會(huì)有偏差,感覺沒什么卵用) 
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0); 
    dispatch_source_set_event_handler(timer, ^{ 
        NSLog(@"EVET ---- %@",[NSThread currentThread]);
        if (tempTimeout <= 0) { 
          // 倒計(jì)時(shí)結(jié)束悼院,取消源 
          dispatch_source_cancel(timer); 
          // 回到主線程更新UI     
          dispatch_async(dispatch_get_main_queue(), ^{ 
              // 倒計(jì)時(shí)結(jié)束時(shí)界面的UI的更新 
              if (endBlock) { 
                  endBlock(); 
              } 
          });
       } else { 
        // 回到主線程更新UI         
        dispatch_async(dispatch_get_main_queue(), ^{ 
          // 做界面的UI的更新
           if (eventBlock) { 
              eventBlock(); 
           }
         });
         tempTimeout--; 
      }
   });
  dispatch_resume(timer);
}

封裝好之后伤为,調(diào)用起來就非常的Easy啦,并且看起來也挺舒服的据途。其實(shí)你可以把上面的方法封裝成一個(gè)工具類方法绞愚。

 [self startTimerWithTimeout:30 eventBlock:^{ 
      NSLog(@"定時(shí)執(zhí)行---%@",[NSThread currentThread]);
 } endBlock:^{
      NSLog(@"結(jié)束執(zhí)行---%@",[NSThread currentThread]); 
}];

15.Dispatch_source 中神奇的數(shù)據(jù)合并

上面介紹了dispatch_source 有多種類型,發(fā)現(xiàn)一種神奇的類型DISPATCH_SOURCE_TYPE_DATA_ADD,這種類型的source 有什么特別之處呢颖医?假如我們并發(fā)執(zhí)行多個(gè)任務(wù)位衩,這種類型的source 會(huì)在任務(wù)完成時(shí),將data 加1熔萧,然后如果主線程比較空閑糖驴,那么event_handler就會(huì)多次調(diào)用僚祷,而如果主線程恰好比較忙碌,那么就會(huì)將任務(wù)合并贮缕,event_handler調(diào)用次數(shù)就會(huì)比較少辙谜。
還是先上一個(gè)代碼范例:

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue); 
dispatch_source_set_event_handler(source, ^{ 
    unsigned long completion = dispatch_source_get_data(source);   
    NSLog(@"完成的任務(wù)個(gè)數(shù):%lu----%@",completion,[NSThread currentThread]); 
    dispatch_sync(dispatch_get_main_queue(), ^{ 
        NSLog(@"更新UI");
    });
});
dispatch_resume(source); 
dispatch_async(queue, ^{ 
    NSLog(@"網(wǎng)絡(luò)線程---%@",[NSThread currentThread]); 
    [NSThread sleepForTimeInterval:2.0]; 
    dispatch_source_merge_data(source, 1);
}); 
dispatch_async(queue, ^{ 
    NSLog(@"網(wǎng)絡(luò)線程---%@",[NSThread currentThread]); 
    [NSThread sleepForTimeInterval:2.0]; 
    dispatch_source_merge_data(source, 1);
});
//打印結(jié)果:
2016-07-13 15:38:45.911 PractiseProject[4130:150701] 網(wǎng)絡(luò)線程---<NSThread: 0x7f9e12c10c90>{number = 2, name = (null)}
2016-07-13 15:38:45.911 PractiseProject[4130:150694] 網(wǎng)絡(luò)線程---<NSThread: 0x7f9e12da6cb0>{number = 3, name = (null)}
2016-07-13 15:38:47.915 PractiseProject[4130:150701] 完成的任務(wù)個(gè)數(shù):2----<NSThread: 0x7f9e12c10c90>{number = 2, name = (null)}
2016-07-13 15:38:47.915 PractiseProject[4130:150659] 更新UI
// 這也是打印結(jié)果:
2016-07-13 15:48:56.601 PractiseProject[4212:155405] 網(wǎng)絡(luò)線程---<NSThread: 0x7fe390e09270>{number = 2, name = (null)}
2016-07-13 15:48:56.601 PractiseProject[4212:155411] 網(wǎng)絡(luò)線程---<NSThread: 0x7fe390e0f800>{number = 3, name = (null)}
2016-07-13 15:48:59.304 PractiseProject[4212:155405] 完成的任務(wù)個(gè)數(shù):1----<NSThread: 0x7fe390e09270>{number = 2, name = (null)}
2016-07-13 15:48:59.330 PractiseProject[4212:155377] 更新UI
2016-07-13 15:49:01.309 PractiseProject[4212:155415] 完成的任務(wù)個(gè)數(shù):1----<NSThread: 0x7fe390e1d630>{number = 4, name = (null)}
2016-07-13 15:49:01.309 PractiseProject[4212:155377] 更新UI

它會(huì)根據(jù)主線程的繁忙與空閑,以及每個(gè)任務(wù)完成時(shí)的時(shí)間感昼,減少返回次數(shù)或者每次返回装哆。使用場景主要可以用在同時(shí)執(zhí)行多個(gè)任務(wù),任務(wù)的完成個(gè)數(shù)這種情況抑诸。

16.Queue-Specific

由于dispatch_get_current_queueAPI的移除烂琴,為了能夠判斷當(dāng)前queue是否是之前創(chuàng)建的queue,我們可以利用dispatch_queue_set_specific和dispatch_get_specific給queue關(guān)聯(lián)一個(gè)context data蜕乡,后面再利用這個(gè)標(biāo)識(shí)獲取到context data。如果可以獲取到說明當(dāng)前上下文是在自己創(chuàng)建的queue中梗夸,如果不能獲取到context data則表示當(dāng)前是在其他隊(duì)列上层玲。使用場景: 自己創(chuàng)建一個(gè)隊(duì)列,然后保證所有的操作都在該隊(duì)列上執(zhí)行反症。XMPP中有比較多的dispatch_queue_set_specific和dispatch_get_specific使用案例辛块。
設(shè)置標(biāo)識(shí)和關(guān)聯(lián)的數(shù)據(jù):

dispatch_queue_t queue = dispatch_queue_create("com.haley.cn", DISPATCH_QUEUE_SERIAL);
const void *queueSpecificKey = @"queueSpecificKey";
dispatch_queue_set_specific(queue, queueSpecificKey, &queueSpecificKey, NULL);

獲取關(guān)聯(lián)數(shù)據(jù):
dispatch_get_specific(queueSpecificKey)

完整的示例:

dispatch_queue_t queue = dispatch_queue_create("com.haley.cn", DISPATCH_QUEUE_SERIAL); 
// 當(dāng)然這里也可以是其他類型的隊(duì)列 
// dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
// dispatch_queue_t queue = dispatch_queue_create("com.haley.cn", DISPATCH_QUEUE_CONCURRENT);
const void *queueSpecificKey = @"queueSpecificKey"; 
dispatch_queue_set_specific(queue, queueSpecificKey, &queueSpecificKey, NULL); 
dispatch_async(queue, ^{
   NSLog(@"異步任務(wù)"); 
   if (dispatch_get_specific(queueSpecificKey)) { 
      NSLog(@"com.haley.cn---1隊(duì)列");
    } else { 
      NSLog(@"---1其他隊(duì)列"); 
    } 
}); 
NSLog(@"主線程,主隊(duì)列"); 
if (dispatch_get_specific(queueSpecificKey)) { 
  NSLog(@"com.haley.cn---2隊(duì)列"); 
} else { 
  NSLog(@"----2其他隊(duì)列"); 
}
// 打印結(jié)果:
2016-07-11 14:30:56.772 PractiseProject[3379:152363] 主線程铅碍,主隊(duì)列
2016-07-11 14:30:56.772 PractiseProject[3379:152363] ----2其他隊(duì)列
2016-07-11 14:30:56.772 PractiseProject[3379:152451] 異步任務(wù)
2016-07-11 14:30:56.773 PractiseProject[3379:152451] com.haley.cn---1隊(duì)列

dispatch_get_specific所處的環(huán)境如果是在目標(biāo)對(duì)列上時(shí)润绵,就可以獲取到關(guān)聯(lián)的數(shù)據(jù),否則就無法獲取關(guān)聯(lián)數(shù)據(jù)胞谈,返回NULL尘盼。看一看XMPP中的使用案例:

- (BOOL)activate:(XMPPStream *)aXmppStream{ 
    __block BOOL result = YES; 
    dispatch_block_t block = ^{ 
        if (xmppStream != nil) { 
          result = NO; 
        } else { 
          xmppStream = aXmppStream; 
          [xmppStream addDelegate:self delegateQueue:moduleQueue];
          [xmppStream registerModule:self]; 
        } }; 
if (dispatch_get_specific(moduleQueueTag)){
     block();
} else {
     dispatch_sync(moduleQueue, block);
     return result;
}

為了保證block是在目標(biāo)隊(duì)列上執(zhí)行烦绳,先判斷當(dāng)前是否在目標(biāo)隊(duì)列上(如果能取到關(guān)聯(lián)數(shù)據(jù)卿捎,則說明在當(dāng)前隊(duì)列上),如果在目標(biāo)隊(duì)列上径密,直接執(zhí)行block午阵,否則就在目標(biāo)隊(duì)列上同步執(zhí)行。

注意死鎖的情況

情形一:在主線程中調(diào)度主隊(duì)列完成一個(gè)同步任務(wù)享扔,會(huì)發(fā)生死鎖底桂。

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    self.view.backgroundColor = [UIColor orangeColor]; 
    dispatch_sync(dispatch_get_main_queue(), ^{ 
        NSLog(@"串行----線程:%@",[NSThread currentThread]); 
    });
}

如上代碼,界面永遠(yuǎn)不會(huì)加載出來惧眠,里面的NSLog永遠(yuǎn)也不會(huì)執(zhí)行籽懦。原因是ViewDidLoad是在主隊(duì)列的主線程中執(zhí)行,執(zhí)行到dispatch_sync 時(shí)會(huì)阻塞住锉试,等待dispatch_sync中的打印任務(wù)執(zhí)行完畢猫十。而dispatch_sync又會(huì)等viewDidLoad執(zhí)行完畢览濒,再開始執(zhí)行,因此就互相等待發(fā)生死鎖拖云。

情形二:在串行隊(duì)列的同步任務(wù)中再執(zhí)行同步任務(wù)贷笛,會(huì)發(fā)生死鎖。

dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serial_queue, ^{ 
    NSLog(@"串行1----線程:%@",[NSThread currentThread]); 
    dispatch_sync(serial_queue, ^{ 
        NSLog(@"串行2----線程:%@",[NSThread currentThread]); 
    });
});

上面示例中的NSLog(@"串行1----線程:%@",[NSThread currentThread]);會(huì)打印.
但是NSLog(@"串行2----線程:%@",[NSThread currentThread]);永遠(yuǎn)也不會(huì)執(zhí)行宙项。
因?yàn)榇嘘?duì)列一次只能執(zhí)行一個(gè)任務(wù)乏苦,執(zhí)行完畢返回后,才會(huì)執(zhí)行下一個(gè)任務(wù)尤筐,而外層任務(wù)的完成需要等待內(nèi)層任務(wù)的結(jié)束汇荐,而內(nèi)層任務(wù)的開始需要等外層任務(wù)結(jié)束。
其實(shí)情形一是情形二的一種特殊情況盆繁。

情形三:在串行隊(duì)列的異步任務(wù)中再嵌套執(zhí)行同步任務(wù)掀淘,也會(huì)發(fā)生死鎖。

dispatch_queue_t serial_queue = dispatch_queue_create("com.haley.com", DISPATCH_QUEUE_SERIAL);
dispatch_async(serial_queue, ^{ 
    NSLog(@"串行異步----線程:%@",[NSThread currentThread]);   
    dispatch_sync(serial_queue, ^{ 
        NSLog(@"串行2----線程:%@",[NSThread currentThread]); 
    });
    [NSThread sleepForTimeInterval:2.0]; 
});

同樣的油昂,由于串行隊(duì)列一次只能執(zhí)行一個(gè)任務(wù)革娄,任務(wù)結(jié)束后,才能執(zhí)行下一個(gè)任務(wù)冕碟。
所以異步任務(wù)的結(jié)束需要等里面同步任務(wù)結(jié)束拦惋,而里面同步任務(wù)的開始需要等外面異步任務(wù)結(jié)束,所以就相互等待安寺,發(fā)生死鎖了厕妖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市挑庶,隨后出現(xiàn)的幾起案子言秸,更是在濱河造成了極大的恐慌,老刑警劉巖挠羔,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件井仰,死亡現(xiàn)場離奇詭異,居然都是意外死亡破加,警方通過查閱死者的電腦和手機(jī)俱恶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來范舀,“玉大人合是,你說我怎么就攤上這事《Щ罚” “怎么了聪全?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辅辩。 經(jīng)常有香客問我难礼,道長娃圆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任蛾茉,我火速辦了婚禮讼呢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谦炬。我一直安慰自己悦屏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布键思。 她就那樣靜靜地躺著础爬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吼鳞。 梳的紋絲不亂的頭發(fā)上看蚜,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音赖条,去河邊找鬼失乾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛纬乍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播裸卫,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼仿贬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了墓贿?” 一聲冷哼從身側(cè)響起茧泪,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎聋袋,沒想到半個(gè)月后队伟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幽勒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年嗜侮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片啥容。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锈颗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咪惠,到底是詐尸還是另有隱情击吱,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布遥昧,位于F島的核電站覆醇,受9級(jí)特大地震影響朵纷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜永脓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一袍辞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧憨奸,春花似錦革屠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至板甘,卻和暖如春党瓮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盐类。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工寞奸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人在跳。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓枪萄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親猫妙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瓷翻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容