GCD簡(jiǎn)介

1.隊(duì)列

  • 串行隊(duì)列洗贰,串行隊(duì)列將任務(wù)以先進(jìn)先出(FIFO)的順序來執(zhí)行愿待,所以串行隊(duì)列經(jīng)常用來做訪問某些特定資源的同步處理。你可以也根據(jù)需要?jiǎng)?chuàng)建多個(gè)隊(duì)列然遏,而這些隊(duì)列相對(duì)其他隊(duì)列都是并發(fā)執(zhí)行的扮超。換句話說取刃,如果你創(chuàng)建了4個(gè)串行隊(duì)列,每一個(gè)隊(duì)列在同一時(shí)間都只執(zhí)行一個(gè)任務(wù)出刷,對(duì)這四個(gè)任務(wù)來說璧疗,他們是相互獨(dú)立且并發(fā)執(zhí)行的。如果需要?jiǎng)?chuàng)建串行隊(duì)列馁龟,一般用dispatch_queue_create這個(gè)方法來實(shí)現(xiàn)崩侠,并指定隊(duì)列類型DISPATCH_QUEUE_SERIAL。

  • 并發(fā)隊(duì)列坷檩,并發(fā)隊(duì)列雖然是能同時(shí)執(zhí)行多個(gè)任務(wù)却音,但這些任務(wù)仍然是按照先到先執(zhí)行(FIFO)的順序來執(zhí)行的。并發(fā)隊(duì)列會(huì)基于系統(tǒng)負(fù)載來合適地選擇并發(fā)執(zhí)行這些任務(wù)矢炼。并發(fā)隊(duì)列一般指的就是全局隊(duì)列(Global queue)系瓢,進(jìn)程中存在四個(gè)全局隊(duì)列:高、中(默認(rèn))句灌、低夷陋、后臺(tái)四個(gè)優(yōu)先級(jí)隊(duì)列欠拾,可以調(diào)用dispatch_get_global_queue函數(shù)傳入優(yōu)先級(jí)來訪問隊(duì)列。當(dāng)然我們也可以用dispatch_queue_create骗绕,并指定隊(duì)列類型DISPATCH_QUEUE_CONCURRENT藐窄,來自己創(chuàng)建一個(gè)并發(fā)隊(duì)列。

  • 主隊(duì)列酬土,與主線程功能相同荆忍。實(shí)際上,提交至main queue的任務(wù)會(huì)在主線程中執(zhí)行撤缴。main queue可以調(diào)用dispatch_get_main_queue()來獲得刹枉。因?yàn)閙ain queue是與主線程相關(guān)的,所以這是一個(gè)串行隊(duì)列腹泌。和其它串行隊(duì)列一樣嘶卧,這個(gè)隊(duì)列中的任務(wù)一次只能執(zhí)行一個(gè)尔觉。它能保證所有的任務(wù)都在主線程執(zhí)行凉袱,而主線程是唯一可用于更新 UI 的線程。

2.任務(wù)

  • 同步任務(wù)侦铜,使用dispatch_sync將任務(wù)加入隊(duì)列专甩。將同步任務(wù)加入串行隊(duì)列,會(huì)順序執(zhí)行钉稍,一般不這樣做并且在一個(gè)任務(wù)未結(jié)束時(shí)調(diào)起其它同步任務(wù)會(huì)死鎖涤躲。將同步任務(wù)加入并行隊(duì)列,會(huì)順序執(zhí)行贡未,但是也沒什么意義种樱。

  • 異步任務(wù),使用dispatch_async將任務(wù)加入隊(duì)列俊卤。將異步任務(wù)加入串行隊(duì)列嫩挤,會(huì)順序執(zhí)行,并且不會(huì)出現(xiàn)死鎖問題消恍。將異步任務(wù)加入并行隊(duì)列岂昭,會(huì)并行執(zhí)行多個(gè)任務(wù),這也是我們最常用的一種方式狠怨。

3.GCD常見的用法和應(yīng)用場(chǎng)景

3.1 dispatch_async(常見的應(yīng)用場(chǎng)景是異步處理耗時(shí)的操作约啊,然后耗時(shí)操作處理完畢后,使用主線程更新UI)

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
    // 一個(gè)異步的任務(wù)佣赖,例如網(wǎng)絡(luò)請(qǐng)求恰矩,耗時(shí)的文件操作等等
    ...
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI刷新
        ...
    });
});

3.2 dispatch_after (常用的應(yīng)用場(chǎng)景是延時(shí)調(diào)用)

dispatch_queue_t queue= dispatch_get_main_queue();
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
    // 在queue里面延遲執(zhí)行的一段代碼
    ...
});

3.3 dispatch_once (常用于單例的創(chuàng)建,只創(chuàng)建一次)

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只執(zhí)行一次的任務(wù)
    ...
});

3.4 dispatch_group (GCD組憎蛤,把一組任務(wù)提交到隊(duì)列中枢里,多個(gè)請(qǐng)求完畢后才處理事情,如多個(gè)網(wǎng)絡(luò)請(qǐng)求完畢會(huì),才去更新UI)

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, ^{
    // 異步任務(wù)1
});

dispatch_group_async(group, queue, ^{
    // 異步任務(wù)2
});

// 等待group中多個(gè)異步任務(wù)執(zhí)行完畢栏豺,做一些事情彬碱,介紹兩種方式

// 方式1(不好,會(huì)卡住當(dāng)前線程)
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
...

// 方式2(比較好)
dispatch_group_notify(group, mainQueue, ^{
    // 任務(wù)完成后奥洼,在主隊(duì)列中做一些操作
    ...
});

3.5 dispatch_barrier_async(和dispatch_group類似巷疼,dispatch_barrier也是異步任務(wù)間的一種同步方式,可以在比如文件的讀寫操作時(shí)使用灵奖,保證讀操作的準(zhǔn)確性嚼沿。另外,有一點(diǎn)需要注意瓷患,dispatch_barrier_sync和dispatch_barrier_async只在自己創(chuàng)建的并發(fā)隊(duì)列上有效骡尽,在全局(Global)并發(fā)隊(duì)列、串行隊(duì)列上擅编,效果跟dispatch_(a)sync效果一樣)

// dispatch_barrier_async的作用可以用一個(gè)詞概括--承上啟下攀细,它保證此前的任務(wù)都先于自己執(zhí)行,此后的任務(wù)也遲于自己執(zhí)行爱态。本例中谭贪,任務(wù)4會(huì)在任務(wù)1、2锦担、3都執(zhí)行完之后執(zhí)行俭识,而任務(wù)5、6會(huì)等待任務(wù)4執(zhí)行完后執(zhí)行洞渔。

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    // 任務(wù)1
    ...
});
dispatch_async(queue, ^{
    // 任務(wù)2
    ...
});
dispatch_async(queue, ^{
    // 任務(wù)3
    ...
});
dispatch_barrier_async(queue, ^{
    // 任務(wù)4
    ...
});
dispatch_async(queue, ^{
    // 任務(wù)5
    ...
});
dispatch_async(queue, ^{
    // 任務(wù)6
    ...
});

3.6 dispatch_apply(dispatch_apply有什么用呢套媚,因?yàn)閐ispatch_apply并行的運(yùn)行機(jī)制,效率一般快于for循環(huán)的類串行機(jī)制(在for一次循環(huán)中的處理任務(wù)很多時(shí)差距比較大)磁椒。比如這可以用來拉取網(wǎng)絡(luò)數(shù)據(jù)后提前算出各個(gè)控件的大小堤瘤,防止繪制時(shí)計(jì)算,提高表單滑動(dòng)流暢性衷快,如果用for循環(huán)宙橱,耗時(shí)較多,并且每個(gè)表單的數(shù)據(jù)沒有依賴關(guān)系蘸拔,所以用dispatch_apply比較好)

// for循環(huán)做一些事情师郑,輸出0123456789
for (int i = 0; i < 10; i ++) {
    NSLog(@"%d", i);
}

// dispatch_apply替換(當(dāng)且僅當(dāng)處理順序?qū)μ幚斫Y(jié)果無影響環(huán)境),輸出順序不定调窍,比如1098673452
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*! dispatch_apply函數(shù)說明
*
*  @brief  dispatch_apply函數(shù)是dispatch_sync函數(shù)和Dispatch Group的關(guān)聯(lián)API
*         該函數(shù)按指定的次數(shù)將指定的Block追加到指定的Dispatch Queue中,并等到全部的處理執(zhí)行結(jié)束
*
*  @param 10    指定重復(fù)次數(shù)  指定10次
*  @param queue 追加對(duì)象的Dispatch Queue
*  @param index 帶有參數(shù)的Block, index的作用是為了按執(zhí)行的順序區(qū)分各個(gè)Block
*
*/
dispatch_apply(10, queue, ^(size_t index) {
    NSLog(@"%zu", index);
});

3.7 dispatch_suspend和dispatch_resume(隊(duì)列的暫停和恢復(fù)宝冕,已添加到隊(duì)列中沒有執(zhí)行的任務(wù)不會(huì)執(zhí)行,直至等到線程恢復(fù)才會(huì)繼續(xù)執(zhí)行)

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_suspend(queue); //暫停隊(duì)列queue
dispatch_resume(queue);  //恢復(fù)隊(duì)列queue

3.8 dispatch_semaphore_signal

dispatch_semaphore 信號(hào)量基于計(jì)數(shù)器的一種多線程同步機(jī)制邓萨。在多個(gè)線程訪問共有資源時(shí)候地梨,會(huì)因?yàn)槎嗑€程的特性而引發(fā)數(shù)據(jù)出錯(cuò)的問題菊卷。

// dispatch_semaphore_signal有兩類用法:a、解決同步問題宝剖;b洁闰、解決有限資源訪問(資源為1,即互斥)問題万细。
// dispatch_semaphore_wait扑眉,若semaphore計(jì)數(shù)為0則等待,大于0則使其減1赖钞。
// dispatch_semaphore_signal使semaphore計(jì)數(shù)加1腰素。

// a、同步問題:輸出肯定為1雪营、2弓千、3。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(1);
dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);
dispatch_semaphore_t semaphore3 = dispatch_semaphore_create(0);

dispatch_async(queue, ^{
    // 任務(wù)1
    dispatch_semaphore_wait(semaphore1, DISPATCH_TIME_FOREVER);
    NSLog(@"1\n");
    dispatch_semaphore_signal(semaphore2);
    dispatch_semaphore_signal(semaphore1);
});

dispatch_async(queue, ^{
    // 任務(wù)2
    dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);
    NSLog(@"2\n");
    dispatch_semaphore_signal(semaphore3);
    dispatch_semaphore_signal(semaphore2);
});

dispatch_async(queue, ^{
    // 任務(wù)3
    dispatch_semaphore_wait(semaphore3, DISPATCH_TIME_FOREVER);
    NSLog(@"3\n");
    dispatch_semaphore_signal(semaphore3);
});

// b献起、有限資源訪問問題:for循環(huán)看似能創(chuàng)建100個(gè)異步任務(wù)洋访,實(shí)質(zhì)由于信號(hào)限制,最多創(chuàng)建10個(gè)異步任務(wù)征唬。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
for (int i = 0; i < 100; i ++) {
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    dispatch_async(queue, ^{
    // 任務(wù)
    ...
    dispatch_semaphore_signal(semaphore);
    });
}

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
如果semaphore計(jì)數(shù)大于等于1.計(jì)數(shù)-1捌显,返回茁彭,程序繼續(xù)運(yùn)行总寒。
如果計(jì)數(shù)為0,則等待理肺。
這里設(shè)置的等待時(shí)間是一直等待摄闸。

dispatch_semaphore_signal(semaphore);
計(jì)數(shù)+1.
在這兩句代碼中間的執(zhí)行代碼,每次只會(huì)允許一個(gè)線程進(jìn)入妹萨,這樣就有效的保證了在多線程環(huán)境下年枕,只能有一個(gè)線程進(jìn)入。

3.9 dispatch_set_context乎完、dispatch_get_context和dispatch_set_finalizer_f(dispatch_set_context可以為隊(duì)列添加上下文數(shù)據(jù)熏兄,但是因?yàn)镚CD是C語言接口形式的,所以其context參數(shù)類型是“void *”树姨。需使用上述abc三種方式創(chuàng)建context摩桶,并且一般結(jié)合dispatch_set_finalizer_f使用,回收context內(nèi)存)

// dispatch_set_context帽揪、dispatch_get_context是為了向隊(duì)列中傳遞上下文context服務(wù)的硝清。
// dispatch_set_finalizer_f相當(dāng)于dispatch_object_t的析構(gòu)函數(shù)。
// 因?yàn)閏ontext的數(shù)據(jù)不是foundation對(duì)象转晰,所以arc不會(huì)自動(dòng)回收芦拿,一般在dispatch_set_finalizer_f中手動(dòng)回收士飒,所以一般講上述三個(gè)方法綁定使用。

- (void)test
{
    // 幾種創(chuàng)建context的方式
    // a蔗崎、用C語言的malloc創(chuàng)建context數(shù)據(jù)酵幕。
    // b硼身、用C++的new創(chuàng)建類對(duì)象死遭。
    // c、用Objective-C的對(duì)象郎任,但是要用__bridge等關(guān)鍵字轉(zhuǎn)為Core Foundation對(duì)象他嫡。

    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    if (queue) {
        // "123"即為傳入的context
        dispatch_set_context(queue, "123");
        dispatch_set_finalizer_f(queue, &xigou);
    }
    dispatch_async(queue, ^{
        char *string = dispatch_get_context(queue);
        NSLog(@"%s", string);
    });
}

// 該函數(shù)會(huì)在dispatch_object_t銷毀時(shí)調(diào)用番官。
void xigou(void *context)
{
    // 釋放context的內(nèi)存(對(duì)應(yīng)上述abc)

    // a、CFRelease(context);
    // b钢属、free(context);
    // c徘熔、delete context;
}

4. 常見的死鎖

4.1 dispatch_sync

// 假設(shè)這段代碼執(zhí)行于主隊(duì)列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t mainQueue = dispatch_get_main_queue();

// 在主隊(duì)列添加同步任務(wù)
dispatch_sync(mainQueue, ^{
    // 任務(wù)
    ...
});

// 在串行隊(duì)列添加同步任務(wù) 
dispatch_sync(serialQueue, ^{
    // 任務(wù)
    ...
    dispatch_sync(serialQueue, ^{
        // 任務(wù)
        ...
    });
};

4.2 dispatch_apply

// 因?yàn)閐ispatch_apply會(huì)卡住當(dāng)前線程,內(nèi)部的dispatch_apply會(huì)等待外部淆党,外部的等待內(nèi)部酷师,所以死鎖。
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(10, queue, ^(size_t) {
    // 任務(wù)
    ...
    dispatch_apply(10, queue, ^(size_t) {
        // 任務(wù)
        ...
    });
});

4.3 dispatch_barrier

dispatch_barrier_sync在串行隊(duì)列和全局并行隊(duì)列里面和dispatch_sync同樣的效果染乌,所以需考慮同dispatch_sync一樣的死鎖問題山孔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市荷憋,隨后出現(xiàn)的幾起案子台颠,更是在濱河造成了極大的恐慌,老刑警劉巖勒庄,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件串前,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡实蔽,警方通過查閱死者的電腦和手機(jī)荡碾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來局装,“玉大人坛吁,你說我怎么就攤上這事☆砩校” “怎么了拨脉?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)塑径。 經(jīng)常有香客問我女坑,道長(zhǎng),這世上最難降的妖魔是什么统舀? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任匆骗,我火速辦了婚禮劳景,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘碉就。我一直安慰自己盟广,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布瓮钥。 她就那樣靜靜地躺著筋量,像睡著了一般。 火紅的嫁衣襯著肌膚如雪碉熄。 梳的紋絲不亂的頭發(fā)上桨武,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音锈津,去河邊找鬼呀酸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛琼梆,可吹牛的內(nèi)容都是我干的性誉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼茎杂,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼错览!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起煌往,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤倾哺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后携冤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悼粮,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡闲勺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年曾棕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菜循。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翘地,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出癌幕,到底是詐尸還是另有隱情衙耕,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布勺远,位于F島的核電站橙喘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏胶逢。R本人自食惡果不足惜厅瞎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一饰潜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧和簸,春花似錦彭雾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至爽柒,卻和暖如春吴菠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浩村。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工橄务, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人穴亏。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓蜂挪,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親嗓化。 傳聞我的和親對(duì)象是個(gè)殘疾皇子棠涮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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