44.通過Dispatch Group機(jī)制,根據(jù)系統(tǒng)資源狀況來執(zhí)行任務(wù)

《編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法》--第六章 第44條
(ps:此乃讀書筆記,加深記憶,僅供大家參考)


第44條:通過Dispatch Group機(jī)制,根據(jù)系統(tǒng)資源狀況來執(zhí)行任務(wù)

dispatch group是GCD的一項(xiàng)特性,能夠把任務(wù)分組。調(diào)用者可以等待這組任務(wù)執(zhí)行完畢,也可以在提供回調(diào)函數(shù)之后繼續(xù)往下執(zhí)行,這組任務(wù)完成時(shí),調(diào)用者會(huì)得到通知。這個(gè)功能有許多用途,其中最重要、最值得注意的用法,就是把將要并發(fā)執(zhí)行的多個(gè)任務(wù)合為一組,于是調(diào)用者就可以知道這些任務(wù)何時(shí)才能全部執(zhí)行完畢。

下面這個(gè)函數(shù)可以創(chuàng)建dispatch group:

dispatch_group_t group = dispatch_group_create();

dispatch group就是個(gè)簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu),這種結(jié)構(gòu)彼此之間沒什么區(qū)別,它不像派發(fā)隊(duì)列,后者還有個(gè)用來區(qū)別身份的標(biāo)識(shí)符。想把任務(wù)編組,有兩種辦法。第一種是下面這個(gè)函數(shù):

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

它是普通的dispatch_async函數(shù)的變體,比原來多一個(gè)函數(shù),用于表示待執(zhí)行的塊所歸屬的組。還有種辦法能夠指定任務(wù)所屬的dispatch group,那就是下面這一對(duì)函數(shù):

dispatch_group_enter(dispatch_group_t group)
dispatch_group_leave(dispatch_group_t group)

前者能夠使分組里正要執(zhí)行的任務(wù)數(shù)遞增,而后者則使之遞減。由此可知,調(diào)用了dispatch_group_enter以后,必須又與之對(duì)應(yīng)的dispatch_group_leave才行。這與引用計(jì)數(shù)(參見第29條)相似,要使用引用計(jì)數(shù)。就必須令保留操作與釋放操作彼此對(duì)應(yīng),以防內(nèi)存泄漏。

下面這個(gè)函數(shù)可用于等待dispatch group執(zhí)行完畢:

void dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout)

此函數(shù)接受兩個(gè)參數(shù),一個(gè)是要等待的group,另一個(gè)是代表等待時(shí)間的timeout值。timeout參數(shù)表示函數(shù)在等待dispatch group執(zhí)行完畢時(shí),應(yīng)該阻塞多久。如果執(zhí)行dispatch group所需的時(shí)間小于timeout,則返回0,否則返回非0值。此參數(shù)也可以取常量DISPATCH_TIME_FOREVER,這表示函數(shù)會(huì)一直等待dispatch group執(zhí)行完,而不會(huì)超時(shí)(time out)。
除了可以用上面那個(gè)函數(shù)等待dispatch group執(zhí)行完畢之外,也可以換個(gè)辦法,使用下列函數(shù):

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

與wait函數(shù)略有不同的是:開發(fā)者可以向此函數(shù)傳入塊,等dispatch group執(zhí)行完畢之后,塊會(huì)在特定的線程上執(zhí)行。加入當(dāng)前此案成不應(yīng)阻塞,而開發(fā)者又想在那些任務(wù)全部完成時(shí)得到通知,那么此做法就很有必要了。

如果想令數(shù)組中的每個(gè)對(duì)象都執(zhí)行某項(xiàng)任務(wù),并且想等待所有任務(wù)執(zhí)行完畢,那么就可以使用這個(gè)GCD特性來實(shí)現(xiàn)。代碼如下:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_group_t dispatchGroup = dispatch_group_create();

NSArray * collection;
for (id object in collection) {
    dispatch_group_async(dispatchGroup, queue, ^{
        [object description];
    });
}

dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER);
//Continue processing after completing tasks

若當(dāng)前線程不應(yīng)阻塞,則可用notify函數(shù)來取代wait:

dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
    //Continue processing after completing tasks
});

notify回調(diào)時(shí)所選用的隊(duì)列,完全應(yīng)該根據(jù)具體情況來定。筆者在范例代碼中使用了主線程隊(duì)列,這種是常見寫法。也可以用自定義的串行隊(duì)列或者全局并發(fā)隊(duì)列。

在本例中,所有任務(wù)都派發(fā)到同一隊(duì)列之中。但實(shí)際上未必一定要這樣做。也可以把某些任務(wù)放在優(yōu)先級(jí)高的線程上執(zhí)行,同時(shí)仍然把所有任務(wù)都?xì)w入同一個(gè)dispatch group,并在執(zhí)行完畢時(shí)獲得通知:

dispatch_queue_t lowPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t highPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

dispatch_group_t dispatchGroup = dispatch_group_create();

NSArray * lowPriorityObject;
NSArray * highPriorityObject;
for (id object in lowPriorityObject) {
    dispatch_group_async(dispatchGroup, lowPriorityQueue, ^{
        [object description];
    });
}

for (id object in highPriorityObject) {
    dispatch_group_async(dispatchGroup, highPriorityQueue, ^{
        [object description];
    });
}


dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
    //Continue processing after completing tasks
});

除了像上面這樣把任務(wù)提交到并發(fā)隊(duì)列之外,也可以把任務(wù)提交至各個(gè)串行隊(duì)列中,并用dispatch group跟蹤其執(zhí)行狀況。然而,如果所有任務(wù)都排在同一個(gè)串行隊(duì)列里面,那么dispatch group就用處不大了。因?yàn)榇藭r(shí),任務(wù)總要逐個(gè)執(zhí)行,所以只需在提交完全部任務(wù)之后再提交一個(gè)塊即可,這樣做與通過notify函數(shù)等待dispatch group執(zhí)行完畢后再回調(diào)塊是等效的:

dispatch_queue_t queue = dispatch_queue_create("com.effectiveobjectivec.queue", NULL);

NSArray *collection;
for (id object in collection) {
    dispatch_async(queue, ^{
        [object description];
    });
}

dispatch_async(queue, ^{
    //Continue processing after completing tasks
});

筆者為何要在標(biāo)題中談到“根據(jù)系統(tǒng)資源狀況來執(zhí)行任務(wù)”呢?回頭看看向并發(fā)隊(duì)列派發(fā)任務(wù)的那個(gè)例子,就會(huì)明白了。為了執(zhí)行隊(duì)列中的塊,GCD會(huì)在適當(dāng)?shù)臅r(shí)機(jī)自動(dòng)創(chuàng)建新線程或或復(fù)用舊線程。如果并發(fā)隊(duì)列,那么其中有可能會(huì)有多個(gè)線程,這也就意味著多個(gè)塊可以并發(fā)執(zhí)行。在并發(fā)隊(duì)列中,執(zhí)行任務(wù)所用的并發(fā)線程數(shù)量,取決于各個(gè)因素,而GCD主要是根據(jù)系統(tǒng)資源狀況來判定這些因素的。加入CPU有多個(gè)核心,并且隊(duì)列中有大量任務(wù)等待執(zhí)行,那那么GCD就可能會(huì)給該隊(duì)列配備多個(gè)線程。通過dispatch group所提供的這種簡(jiǎn)便方式,既可以并發(fā)執(zhí)行一系列給定的任務(wù),又能在全部任務(wù)結(jié)束時(shí)得到通知。由于GCD有并發(fā)隊(duì)列機(jī)制,所以能夠根據(jù)可用的系統(tǒng)資源狀況來并發(fā)執(zhí)行任務(wù)。

在前面的范例代碼中,我們遍歷某個(gè)collection,并在其每個(gè)元素上執(zhí)行任務(wù),而這也可以用另外一個(gè)GCD函數(shù)來實(shí)現(xiàn):

void dispatch_apply(size_t iterations, dispatch_queue_t queue,void (^block)(size_t));

此函數(shù)將塊反復(fù)執(zhí)行一定的次數(shù),每次傳給塊的參數(shù)值都會(huì)遞增,從0開始,直至“iterations-1”。其用法如下:

dispatch_queue_t queue = dispatch_queue_create("com.effectiveobjectivec.queue", NULL);
dispatch_apply(10, queue, ^(size_t i) {
    //Perform task
});

有一件事要注意:dispatch_apply所用的隊(duì)列可以是并發(fā)隊(duì)列。如果采用并發(fā)隊(duì)列,那么系統(tǒng)就可以根據(jù)資源狀況來并行執(zhí)行這些塊了,這與使用dispatch group的那段范例代碼一樣。

這再次表明,未必總要使用dispatch group。然而,dispatch_apply會(huì)持續(xù)阻塞,直到所有任務(wù)都執(zhí)行完畢為止。由此可見,假如把塊派給了當(dāng)前隊(duì)列(或者體系中高于當(dāng)前隊(duì)列的某個(gè)串行隊(duì)列),就將導(dǎo)致死鎖。若想在后臺(tái)執(zhí)行任務(wù),則應(yīng)使用dispatch group。

要點(diǎn)

  • 一系列任務(wù)可歸入一個(gè)dispatch group之中。開發(fā)者可以在這組任務(wù)執(zhí)行完畢時(shí)獲得通知。
  • 通過dispatch group,可以在并發(fā)式派發(fā)隊(duì)列里同時(shí)執(zhí)行多項(xiàng)任務(wù)。此時(shí),GCD會(huì)根據(jù)系統(tǒng)資源狀況來調(diào)度這些并發(fā)執(zhí)行的任務(wù)。開發(fā)者若自己來實(shí)現(xiàn)此功能,則需編寫大量代碼。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容