iOS GCD詳細(xì)介紹

簡(jiǎn)介

GCD(Grand Central Dispatch)是在macOS10.6提出來(lái)的圆存,后來(lái)在iOS4.0被引入逗抑。GCD的引入主要是它的使用比傳統(tǒng)的多線程方案如NSThread学辱、NSOperationQueue乃戈、NSInvocationOperation使用起來(lái)更加方便脯宿,并且GCD的運(yùn)作是在系統(tǒng)級(jí)實(shí)現(xiàn)的话告。由于是作為系統(tǒng)的一部分來(lái)實(shí)現(xiàn)的兼搏,因此比以前的線程更加有效。
同時(shí)GCD使用了block語(yǔ)法沙郭,在書寫上變得更加簡(jiǎn)潔佛呻。
至于什么是多線程,多線程編程的優(yōu)缺點(diǎn)這里就不探討了病线,主要討論一下GCD的使用吓著。

Dispatch Queue介紹

關(guān)于GCD,蘋果所給出最直接的描述是:將想要執(zhí)行的任務(wù)添加到Dispatch Queue中送挑。因此Dispatch Queue將是接下來(lái)討論的關(guān)鍵绑莺。
先來(lái)看下面這段代碼:

dispatch_async(queue, ^{
    // 要執(zhí)行的任務(wù) 
});

dispatch_async()是向隊(duì)列中添加任務(wù)的函數(shù)。這段代碼是將要執(zhí)行的任務(wù)以block代碼塊的形式作為參數(shù)惕耕,添加到queue的隊(duì)列中纺裁,而queue則會(huì)按照順序處理隊(duì)列中的任務(wù)

另外司澎,Dispatch Queue以處理方式的不同欺缘,分為兩種:

  • Serial Dispatch Queue,順序依次執(zhí)行挤安,只有隊(duì)列中前一個(gè)任務(wù)執(zhí)行完成谚殊,后一個(gè)才可以開始。也就是我們常說(shuō)的串行隊(duì)列蛤铜。
  • Concurrent Dispatch Queue络凿,并發(fā)執(zhí)行,將隊(duì)列中的任務(wù)依次添加到并行的線程中昂羡,同時(shí)執(zhí)行。也就是我們常說(shuō)的并行隊(duì)列摔踱。??注意:能夠同時(shí)執(zhí)行任務(wù)的個(gè)數(shù)取決于系統(tǒng)當(dāng)前的處理能力虐先。

??注意:Dispatch Queue隊(duì)列并不是指我們印象中的線程,它是任務(wù)隊(duì)列派敷,它只負(fù)責(zé)任務(wù)的管理調(diào)度蛹批,并不進(jìn)行任務(wù)的執(zhí)行操作撰洗,任務(wù)的執(zhí)行是由Dispatch Queue分配的線程來(lái)完成的

Dispatch Queue創(chuàng)建

在了解了什么是Dispatch Queue后,來(lái)看一下Dispatch Queue是如何得到的腐芍,先來(lái)看一段代碼:

dispatch_queue_t aSerialDispatchQueue =
dispatch_queue_create("MySerialDispatchQueue", NULL);

這段代碼就是通過dispatch_queue_create()函數(shù)得到一個(gè)Dispatch Queue差导。

其中,第一個(gè)參數(shù)是指Dispatch Queue的名稱猪勇,可以設(shè)置為NULL但是不建議這樣做设褐,因?yàn)樵?code>Xcode和Instruments調(diào)試的時(shí)候都會(huì)以設(shè)置的這個(gè)參數(shù)作為展示名稱,所以建議創(chuàng)建的每一個(gè)Dispatch Queue都設(shè)置一個(gè)合適的名稱泣刹;

函數(shù)的第二個(gè)參數(shù)設(shè)置成了NULL助析,此時(shí)得到的是Serial Dispatch Queue類型的隊(duì)列,也可以直接設(shè)置第二個(gè)參數(shù)為DISPATCH_QUEUE_SERIAL椅您,就像這樣:

dispatch_queue_t aSerialDispatchQueue =
dispatch_queue_create("MySerialDispatchQueue", DISPATCH_QUEUE_SERIAL);

如果我們想得到一個(gè)Concurrent Dispatch Queue類型的隊(duì)列外冀,第二個(gè)參數(shù)要設(shè)置為DISPATCH_QUEUE_CONCURRENT,就像這樣:

dispatch_queue_t aConcurrentDispatchQueue =
dispatch_queue_create("MyConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT);

得到的返回值的類型都為dispatch_queue_t掀泳。

驗(yàn)證

下面用代碼來(lái)驗(yàn)證一下這兩種隊(duì)列是否像上邊說(shuō)的那樣執(zhí)行的雪隧。
先來(lái)驗(yàn)證一下Serial Dispatch Queue

dispatch_queue_t serialQueue 
= dispatch_queue_create("queue_1", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
    NSLog(@"任務(wù)1 begin");
    [NSThread sleepForTimeInterval:3.f];
    NSLog(@"任務(wù)1 stop");
});
dispatch_async(serialQueue, ^{
    NSLog(@"任務(wù)2 begin");
    [NSThread sleepForTimeInterval:2.f];
    NSLog(@"任務(wù)2 stop");
});
dispatch_async(serialQueue, ^{
    NSLog(@"任務(wù)3 begin");
    [NSThread sleepForTimeInterval:1.f];
    NSLog(@"任務(wù)3 stop");
});

看一下打印結(jié)果:


再來(lái)驗(yàn)證一下Concurrent Dispatch Queue

dispatch_queue_t concurrentQueue 
= dispatch_queue_create("queue_2", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(concurrentQueue, ^{
    NSLog(@"任務(wù)1 begin");
    [NSThread sleepForTimeInterval:3.f];
    NSLog(@"任務(wù)1 stop");
});
dispatch_async(concurrentQueue, ^{
    NSLog(@"任務(wù)2 begin");
    [NSThread sleepForTimeInterval:2.f];
    NSLog(@"任務(wù)2 stop");
});
dispatch_async(concurrentQueue, ^{
    NSLog(@"任務(wù)3 begin");
    [NSThread sleepForTimeInterval:1.f];
    NSLog(@"任務(wù)3 stop");
});

看一下打印結(jié)果:


多個(gè)Dispatch Queue之間的關(guān)系

通過上面的驗(yàn)證確實(shí)可以看出Serial Dispatch Queue是串行執(zhí)行、Concurrent Dispatch Queue是并行執(zhí)行的员舵。那如果我們創(chuàng)建多個(gè)Serial Dispatch Queue會(huì)怎樣呢脑沿,這些Serial Dispatch Queue也會(huì)按照順序依次執(zhí)行么?不是的固灵,它們之間是并發(fā)執(zhí)行的捅伤,也就是說(shuō)多個(gè)Dispatch Queue之間是并發(fā)執(zhí)行


那如果想讓多個(gè)Serial Dispatch Queue依然保持串行執(zhí)行怎么辦呢?后邊會(huì)繼續(xù)說(shuō)巫玻。

Dispatch Queue持有與釋放

macOS10.8iOS6.0以后丛忆,GCD已經(jīng)支持ARC模式了,所以無(wú)需手動(dòng)管理Dispatch Queue的持有與釋放仍秤。
這里提一下MRC模式下管理Dispatch Queue的兩個(gè)函數(shù):

dispatch_retain(aSerialDispatchQueue)熄诡;
dispatch_release(aSerialDispatchQueue);

系統(tǒng)提供的Dispatch Queue

除了我們手動(dòng)創(chuàng)建的Dispatch Queue以外,系統(tǒng)還給我們提供了幾個(gè)現(xiàn)成的隊(duì)列诗力,Main Dispatch QueueGlobal Dispatch Queue

  • Main Dispatch Queue是在主線程中執(zhí)行的Dispatch Queue凰浮。因?yàn)橹骶€程只有一條,并且主線程中的任務(wù)是依次執(zhí)行的苇本,所以Main Dispatch Queue自然是Serial Dispatch Queue類型的隊(duì)列袜茧,追加到Main Dispatch Queue的任務(wù)都是在主線程RunLoop中執(zhí)行的,像界面更新等一些任務(wù)也都是在這個(gè)線程中執(zhí)行瓣窄。
    獲得方法:
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
  • Global Dispatch Queue是所有應(yīng)用程序都能使用的Concurrent Dispatch Queue類型隊(duì)列笛厦。Global Dispatch Queue有四個(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)
// 高優(yōu)先級(jí)
dispatch_queue_t globalDispatchQueueHigh 
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
// 默認(rèn)優(yōu)先級(jí)
dispatch_queue_t globalDispatchQueueDefault 
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 低優(yōu)先級(jí)
dispatch_queue_t globalDispatchQueueLow 
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
// 后臺(tái)優(yōu)先級(jí)
dispatch_queue_t globalDispatchQueueBackground 
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

對(duì)于Main Dispatch QueueGlobal Dispatch Queue贱鄙,即使在MRC模式下,也無(wú)需考慮持有與釋放問題姨谷。即使執(zhí)行dispatch_retain()逗宁、dispatch_release()函數(shù)也是不會(huì)發(fā)生任何變化的。

Dispatch Queue目標(biāo)隊(duì)列

GCD中的dispatch_set_target_queue()函數(shù)可以將一個(gè)dispatch_object_t對(duì)象設(shè)置到目標(biāo)隊(duì)列來(lái)處理梦湘,上邊說(shuō)到的dispatch_queue_t都屬于dispatch_object_t對(duì)象瞎颗。

上邊曾說(shuō)過多個(gè)Serial Dispatch Queue之間是并行執(zhí)行的,先來(lái)驗(yàn)證一下:

dispatch_queue_t queue1 
= dispatch_queue_create("queue_1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 
= dispatch_queue_create("queue_2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 
= dispatch_queue_create("queue_3", DISPATCH_QUEUE_SERIAL);

dispatch_async(queue1, ^{
    NSLog(@"任務(wù)1 begin");
    [NSThread sleepForTimeInterval:3.f];
    NSLog(@"任務(wù)1 stop");
});
dispatch_async(queue2, ^{
    NSLog(@"任務(wù)2 begin");
    [NSThread sleepForTimeInterval:2.f];
    NSLog(@"任務(wù)2 stop");
});
dispatch_async(queue3, ^{
    NSLog(@"任務(wù)3 begin");
    [NSThread sleepForTimeInterval:1.f];
    NSLog(@"任務(wù)3 stop");
});

打印結(jié)果:


通過打印結(jié)果來(lái)看践叠,雖然創(chuàng)建的是3個(gè)串行Dispatch Queue言缤,但是串行的Dispatch Queue間卻是并行執(zhí)行關(guān)系。
如果我們將創(chuàng)建好的這3個(gè)Serial Dispatch Queue隊(duì)列添加到一個(gè)目標(biāo)隊(duì)列中禁灼,它們的執(zhí)行順序又會(huì)怎樣呢:

dispatch_queue_t targetQueue 
= dispatch_queue_create("target_queue", DISPATCH_QUEUE_SERIAL);

dispatch_queue_t queue1 = dispatch_queue_create("queue_1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("queue_2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 = dispatch_queue_create("queue_3", DISPATCH_QUEUE_SERIAL);

dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_set_target_queue(queue3, targetQueue);

dispatch_async(queue1, ^{
    NSLog(@"任務(wù)1 begin");
    [NSThread sleepForTimeInterval:3.f];
    NSLog(@"任務(wù)1 stop");
});
dispatch_async(queue2, ^{
    NSLog(@"任務(wù)2 begin");
    [NSThread sleepForTimeInterval:2.f];
    NSLog(@"任務(wù)2 stop");
});
dispatch_async(queue3, ^{
    NSLog(@"任務(wù)3 begin");
    [NSThread sleepForTimeInterval:1.f];
    NSLog(@"任務(wù)3 stop");
});

打印結(jié)果:


通過打印可以看出管挟,被添加到目標(biāo)隊(duì)列里的3個(gè)隊(duì)列,按照串行順序執(zhí)行弄捕。其實(shí)是串行執(zhí)行還是并行執(zhí)行跟目標(biāo)隊(duì)列的性質(zhì)有關(guān)僻孝。

如果將targetQueue換成一個(gè)并行隊(duì)列,相信被執(zhí)行的3個(gè)隊(duì)列必然是并行執(zhí)行關(guān)系守谓,我已經(jīng)做了驗(yàn)證:


繼續(xù)穿铆,現(xiàn)在我們將目標(biāo)隊(duì)列targetQueue換回成Serial Dispatch Queue串行隊(duì)列,而將3個(gè)被添加的隊(duì)列換成Concurrent Dispatch Queue并行隊(duì)列斋荞,并分別向其中額外再添加2個(gè)任務(wù)荞雏,此時(shí)3個(gè)被添加隊(duì)列中分別包含的任務(wù)是:1-11-2平酿、1-3凤优;2-12-2蜈彼、2-3筑辨;3-13-2幸逆、3-3棍辕,再來(lái)看一下打印結(jié)果:

通過打印結(jié)果發(fā)現(xiàn),所有任務(wù)都是按照串行順序執(zhí)行下來(lái)的还绘,被添加的三個(gè)并行隊(duì)列本身的并行特性被失效了楚昭。

還沒有完,如果把目標(biāo)隊(duì)列換成并行的Concurrent Dispatch Queue又會(huì)怎樣呢拍顷?


通過打印結(jié)果可以看出抚太,所有的任務(wù)都被并行執(zhí)行。

通過上面的測(cè)試可以看出:無(wú)論被添加的是什么菇怀、什么隊(duì)列凭舶,它們所包含的任務(wù)(當(dāng)然這些任務(wù)都是沒有被原所在隊(duì)列執(zhí)行的)最終都會(huì)按照目標(biāo)隊(duì)列的自身性質(zhì)來(lái)執(zhí)行,它們的優(yōu)先級(jí)也遵循目標(biāo)隊(duì)列的優(yōu)先級(jí)爱沟。

利用dispatch_queue_create()函數(shù)生成的Dispatch Queue不管是Serial Dispatch Queue還是Concurrent Dispatch Queue所使用的都是與Global Dispatch Queue默認(rèn)優(yōu)先級(jí)相同優(yōu)先級(jí)的線程帅霜,利用dispatch_set_target_queue()函數(shù)我們可以改變它們的優(yōu)先級(jí)。

貼一下官方文檔(翻譯不好呼伸,只能靠你的英文功力了):


延遲追加任務(wù)

dispatch_queue_t mainDispatchQueue =  dispatch_get_main_queue();
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3* NSEC_PER_SEC);
dispatch_after(time,mainDispatchQueue,^{
    // 任務(wù)...
});

以上代碼段是將任務(wù)延遲3秒添加到隊(duì)列中身冀,注意的是:是添加到隊(duì)列中而不是執(zhí)行
第一個(gè)參數(shù)timedispatch_time_t類型括享,該類型值可通過dispatch_time()函數(shù)或dispatch_walltime()函數(shù)得到搂根。
dispatch_time()函數(shù)的含義是獲得從第一個(gè)參數(shù)指定的時(shí)間開始,經(jīng)過第二個(gè)參數(shù)指定的時(shí)間長(zhǎng)度后的時(shí)間铃辖。DISPATCH_TIME_NOW表示現(xiàn)在的時(shí)間剩愧,類型為dispatch_time_tNSEC_PER_SEC為秒的單位娇斩,NSEC_PER_MSEC為毫秒單位仁卷。dispatch_walltime()函數(shù)用于計(jì)算絕對(duì)時(shí)間。

Dispatch Group

在實(shí)際應(yīng)用中犬第,經(jīng)常需要在執(zhí)行完一些任務(wù)后锦积,再執(zhí)行某一個(gè)特定任務(wù)。如果使用的是Serial Dispatch Queue只需將任務(wù)全部添加到隊(duì)列中歉嗓,然后再在最后追加上想要執(zhí)行的任務(wù)就可以了丰介。但是在使用Concurrent Dispatch Queue類型的隊(duì)列或者同時(shí)使用多個(gè)Dispatch Queue的時(shí)候,想實(shí)現(xiàn)這樣的需求就比較困難了鉴分。
這時(shí)哮幢,就要用到Dispatch Group了。下面通過代碼來(lái)看一下Dispatch Group是如何使用的:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group,queue, ^{NSLog(@"任務(wù)1");});
dispatch_group_async(group,queue, ^{NSLog(@"任務(wù)2");});
dispatch_group_async(group,queue, ^{NSLog(@"任務(wù)3");});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"最后要執(zhí)行的任務(wù)");})
  • group是通過dispatch_group_create()函數(shù)創(chuàng)建的冠场,類型為dispatch_group_t家浇;
  • dispatch_group_async()函數(shù)與dispatch_async()函數(shù)相同,都是向隊(duì)列中追加任務(wù)碴裙,不同的是dispatch_group_async()函數(shù)中第一個(gè)參數(shù)是指定當(dāng)前任務(wù)屬于哪個(gè)Dispatch Group钢悲;
  • dispatch_group_notify()函數(shù)中第一個(gè)參數(shù)是指定要監(jiān)視的Dispatch Group,在屬于該group的所有任務(wù)都執(zhí)行完成后會(huì)將函數(shù)的第三個(gè)參數(shù)任務(wù)追加到第二個(gè)參數(shù)隊(duì)列中執(zhí)行舔株。

除了添加對(duì)任務(wù)的監(jiān)控以外莺琳,還可使用等待函數(shù),來(lái)看下面一段代碼:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue,^{NSLog(@"任務(wù)1");});
dispatch_group_async(group, queue,^{NSLog(@"任務(wù)2");});
dispatch_group_async(group, queue,^{NSLog(@"任務(wù)3");});

dispatch_group_wait(group, 10*NSEC_PER_SEC);

上面代碼中的dispatch_group_wait()函數(shù)是對(duì)所屬group的任務(wù)的處理過程進(jìn)行等待载慈,函數(shù)中第二個(gè)參數(shù)代表等待時(shí)間惭等,為dispatch_time_t類型。如果在設(shè)置時(shí)間內(nèi)所有任務(wù)執(zhí)行完成函數(shù)返回long類型的值0办铡,如果返回值不為0說(shuō)明還有任務(wù)在執(zhí)行中辞做。如果第二個(gè)參數(shù)設(shè)置為DISPATCH_TIME_FOREVER琳要,函數(shù)必將返回0,因?yàn)樵摵瘮?shù)將無(wú)限期掛起等待秤茅,直到所有任務(wù)執(zhí)行完成函數(shù)才會(huì)返回稚补。
那么等待到底意味著什么?這意味著一旦調(diào)用dispatch_group_wait()函數(shù)框喳,該函數(shù)就處于調(diào)用狀態(tài)而不返回课幕,即執(zhí)行dispatch_group_wait()函數(shù)的所在線程停止。當(dāng)該函數(shù)返回值后五垮,當(dāng)前線程繼續(xù)乍惊。
如果將函數(shù)中第二個(gè)參數(shù)設(shè)置為DISPATCH_TIME_NOW,則不需要等待即可判定所屬group的任務(wù)是否全部執(zhí)行完成放仗。

dispatch_barrier_async()函數(shù)

通常在進(jìn)行數(shù)據(jù)讀润绎、寫操作的時(shí)候,多個(gè)任務(wù)同時(shí)執(zhí)行讀操作是可以的匙监,但是多個(gè)任務(wù)同時(shí)執(zhí)行寫操作可能就會(huì)發(fā)生數(shù)據(jù)競(jìng)爭(zhēng)的問題凡橱。尤其在一系列復(fù)雜的讀寫操作中,使用Serial Dispatch Queue會(huì)導(dǎo)致讀操作效率變低亭姥,使用Concurrent Dispatch Queue不但會(huì)引起多個(gè)寫任務(wù)發(fā)生數(shù)據(jù)競(jìng)爭(zhēng)稼钩,還可能因?yàn)椴l(fā)執(zhí)行導(dǎo)致讀寫順序錯(cuò)亂。
因此要使用dispatch_barrier_async()函數(shù)配合Concurrent Dispatch Queue并行隊(duì)列來(lái)解決這個(gè)問題达罗。
來(lái)看下面一段代碼:

dispatch_queue_t  queue = dispatch_create_queue("OneConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, block_mission1_reading);
dispatch_async(queue, block_mission2_reading);
dispatch_async(queue, block_mission3_reading);
dispatch_async(queue, block_mission4_reading);

dispatch_barrier_async(queue, block_mission5_writing);

dispatch_async(queue, block_mission6_reading);
dispatch_async(queue, block_mission7_reading);
dispatch_async(queue, block_mission8_reading);

dispatch_barrier_async()函數(shù)會(huì)等到
block_mission1_reading坝撑、
block_mission2_reading
block_mission3_reading粮揉、
block_mission4_reading這些任務(wù)并行執(zhí)行完畢后再將block_mission5_writing任務(wù)追加到隊(duì)列中巡李,當(dāng)dispatch_barrier_async()函數(shù)追加的任務(wù)執(zhí)行完成,隊(duì)列會(huì)恢復(fù)為一般動(dòng)作扶认,繼續(xù)并行處理后續(xù)追加到隊(duì)列中的任務(wù)侨拦。

Dispatch Queue掛起與恢復(fù)

當(dāng)我們想掛起某一個(gè)Dispatch Queue時(shí)

dispatch_suspend(queue);

恢復(fù)

dispatch_resume(queue);

當(dāng)Dispatch Queue掛起后,追加到隊(duì)列中但還沒有執(zhí)行的任務(wù)在這之后停止執(zhí)行辐宾,恢復(fù)后這些任務(wù)繼續(xù)執(zhí)行狱从。

指定任務(wù)只執(zhí)行一次

通過dispatch_once()函數(shù)指定的任務(wù)只執(zhí)行一次,像單例的初始化就可以用該函數(shù)來(lái)實(shí)現(xiàn)叠纹。
正常我們書寫單例的方法是:

static NSObject obj = nil;
@synchronized (self) {
    if (obj == nil) {
        obj = ...
    }
}

使用dispatch_once()函數(shù)的實(shí)現(xiàn)方式是:

static NSObject obj = nil;
static dispatch_once_t pred;
dispatch_once( &pred, ^{
    obj = ...
});

使用dispatch_once()函數(shù)可以保證在多線程環(huán)境下百分之百安全季研。

dispatch_sync()與dispatch_async()的區(qū)別

前面使用頻率特別高的添加任務(wù)函數(shù)dispatch_async(),該函數(shù)是非同步的誉察,它只負(fù)責(zé)將任務(wù)添加到隊(duì)列中与涡,并不在乎添加到隊(duì)列中的任務(wù)是否處理完成,立刻返回。
而相對(duì)于dispatch_async()函數(shù)的dispatch_sync()函數(shù)是同步的驼卖,dispatch_sync()函數(shù)不但負(fù)責(zé)將任務(wù)添加到隊(duì)列中氨肌,還要等待添加的任務(wù)執(zhí)行完成再返回,在此過程中調(diào)用dispatch_sync()函數(shù)所在的線程被掛起酌畜,直到dispatch_sync()函數(shù)返回儒飒,線程恢復(fù),注意是調(diào)用dispatch_sync()函數(shù)的線程被掛起檩奠。

關(guān)于dispatch_sync()函數(shù)比較重要的一個(gè)問題就是死鎖,為什么會(huì)出現(xiàn)死鎖的情況呢附帽?比如說(shuō)有一個(gè)串行隊(duì)列埠戳,并且dispatch_sync()函數(shù)的調(diào)用也是在該隊(duì)列中,這樣串行隊(duì)列的線程在調(diào)用dispatch_sync()函數(shù)的時(shí)候被掛起蕉扮,而線程被掛起之后dispatch_sync()函數(shù)添加的任務(wù)一直得不到線程的處理整胃,一直不能返回,所以線程將一直處于被掛起的狀態(tài)喳钟。
出現(xiàn)這種狀況的核心問題就是(可能有點(diǎn)繞):調(diào)用dispatch_sync()函數(shù)的線程(注意是線程屁使,而不是隊(duì)列,并行隊(duì)列有多個(gè)線程可能并不會(huì)發(fā)生這種狀況奔则,除非調(diào)用函數(shù)的任務(wù)和函數(shù)追加的任務(wù)被分配到并行隊(duì)列中同一線程中去)和處理函數(shù)追加的任務(wù)的線程是同一個(gè)線程蛮寂。此時(shí)就會(huì)發(fā)生死鎖。
舉兩個(gè)例子體會(huì)一下:

dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_sync(mainQueue, ^{NSLog(@"任務(wù)");});
// 死鎖
dispatch_queue_t queue = dispatch_queue_create("OneSerialDispatchQueue", NULL);
dispatch_async(queue, ^{
    dispatch_sync(queue, ^{NSLog(@"任務(wù)");});    
});
// 死鎖

dispatch_apply()函數(shù)

該函數(shù)作用是一次性向隊(duì)列中添加多個(gè)任務(wù)易茬,并且跟dispatch_sync()函數(shù)的使用方式一致酬蹋,是同步的,只有向隊(duì)列中添加的所有任務(wù)都執(zhí)行完成才返回抽莱。并且dispatch_apply()函數(shù)向隊(duì)列中追加的block任務(wù)都是帶有參數(shù)的范抓,這是為了函數(shù)將添加序號(hào)作為參數(shù)傳遞給block任務(wù)。
看下面一段代碼:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(8, queue, ^(size_t index){
    NSLog(@"這是第%zu任務(wù)", index);    
});
NSLog(@"所有任務(wù)處理完成");

執(zhí)行結(jié)果:


雖然我這里的執(zhí)行結(jié)果是順序的食铐,但也有可能執(zhí)行的結(jié)果是無(wú)序的匕垫,因?yàn)檫@里使用的是并行隊(duì)列。但無(wú)論前8個(gè)任務(wù)的順序是怎樣所有任務(wù)處理完成這個(gè)任務(wù)一定是最后一個(gè)執(zhí)行虐呻。
相信理解了同步概念就一定會(huì)明白其中原因了象泵。

寫了這么多,必然不乏漏洞和錯(cuò)誤铃慷,歡迎大家指正单芜。

版權(quán)聲明:出自MajorLMJ技術(shù)博客的原創(chuàng)作品 ,轉(zhuǎn)載時(shí)必須注明出處及相應(yīng)鏈接犁柜!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末洲鸠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌扒腕,老刑警劉巖绢淀,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異瘾腰,居然都是意外死亡皆的,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門蹋盆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)费薄,“玉大人,你說(shuō)我怎么就攤上這事栖雾±懵眨” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵析藕,是天一觀的道長(zhǎng)召廷。 經(jīng)常有香客問我,道長(zhǎng)账胧,這世上最難降的妖魔是什么竞慢? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮治泥,結(jié)果婚禮上筹煮,老公的妹妹穿的比我還像新娘。我一直安慰自己居夹,他們只是感情好寺谤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吮播,像睡著了一般变屁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上意狠,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天粟关,我揣著相機(jī)與錄音,去河邊找鬼环戈。 笑死闷板,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的院塞。 我是一名探鬼主播遮晚,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拦止!你這毒婦竟也來(lái)了县遣?” 一聲冷哼從身側(cè)響起糜颠,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎萧求,沒想到半個(gè)月后其兴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡夸政,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年元旬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片守问。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡匀归,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耗帕,到底是詐尸還是另有隱情朋譬,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布兴垦,位于F島的核電站,受9級(jí)特大地震影響字柠,放射性物質(zhì)發(fā)生泄漏探越。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一窑业、第九天 我趴在偏房一處隱蔽的房頂上張望钦幔。 院中可真熱鬧,春花似錦常柄、人聲如沸鲤氢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)卷玉。三九已至,卻和暖如春喷市,著一層夾襖步出監(jiān)牢的瞬間相种,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工品姓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寝并,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓腹备,卻偏偏與公主長(zhǎng)得像衬潦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子植酥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

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

  • 簡(jiǎn)介 GCD(Grand Central Dispatch)是在macOS10.6提出來(lái)的镀岛,后來(lái)在iOS4.0被引...
    sunmumu1222閱讀 866評(píng)論 0 2
  • 背景 擔(dān)心了兩周的我終于輪到去醫(yī)院做胃鏡檢查了!去的時(shí)候我都想好了最壞的可能(胃癌),之前在網(wǎng)上查的癥狀都很相似哎媚。...
    Dely閱讀 9,239評(píng)論 21 42
  • 本篇博客共分以下幾個(gè)模塊來(lái)介紹GCD的相關(guān)內(nèi)容: 多線程相關(guān)概念 多線程編程技術(shù)的優(yōu)缺點(diǎn)比較喇伯? GCD中的三種隊(duì)列...
    dullgrass閱讀 37,854評(píng)論 30 236
  • iOS中GCD的使用小結(jié) 作者dullgrass 2015.11.20 09:41*字?jǐn)?shù) 4996閱讀 20199...
    DanDanC閱讀 832評(píng)論 0 0
  • 本篇博客共分以下幾個(gè)模塊來(lái)介紹GCD的相關(guān)內(nèi)容: 多線程相關(guān)概念 多線程編程技術(shù)的優(yōu)缺點(diǎn)比較? GCD中的三種隊(duì)列...
    有夢(mèng)想的老伯伯閱讀 1,020評(píng)論 0 4