GCD簡(jiǎn)介

1.隊(duì)列

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

  • 并發(fā)隊(duì)列,并發(fā)隊(duì)列雖然是能同時(shí)執(zhí)行多個(gè)任務(wù),但這些任務(wù)仍然是按照先到先執(zhí)行(FIFO)的順序來(lái)執(zhí)行的。并發(fā)隊(duì)列會(huì)基于系統(tǒng)負(fù)載來(lái)合適地選擇并發(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í)來(lái)訪問(wèn)隊(duì)列。當(dāng)然我們也可以用dispatch_queue_create,并指定隊(duì)列類型DISPATCH_QUEUE_CONCURRENT,來(lái)自己創(chuàng)建一個(gè)并發(fā)隊(duì)列。

  • 主隊(duì)列,與主線程功能相同。實(shí)際上,提交至main queue的任務(wù)會(huì)在主線程中執(zhí)行。main queue可以調(diào)用dispatch_get_main_queue()來(lái)獲得。因?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í)行,但是也沒(méi)什么意義。

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

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

3.1 dispatch_async(常見(jiàn)的應(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í)差距比較大)。比如這可以用來(lái)拉取網(wǎng)絡(luò)數(shù)據(jù)后提前算出各個(gè)控件的大小,防止繪制時(shí)計(jì)算,提高表單滑動(dòng)流暢性,如果用for循環(huán),耗時(shí)較多,并且每個(gè)表單的數(shù)據(jù)沒(méi)有依賴關(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é)果無(wú)影響環(huán)境),輸出順序不定,比如1098673452
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*! dispatch_apply函數(shù)說(shuō)明
*
*  @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ì)列中沒(méi)有執(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è)線程訪問(wèn)共有資源時(shí)候,會(huì)因?yàn)槎嗑€程的特性而引發(fā)數(shù)據(jù)出錯(cuò)的問(wèn)題。

// dispatch_semaphore_signal有兩類用法:a、解決同步問(wèn)題;b、解決有限資源訪問(wèn)(資源為1,即互斥)問(wèn)題。
// dispatch_semaphore_wait,若semaphore計(jì)數(shù)為0則等待,大于0則使其減1。
// dispatch_semaphore_signal使semaphore計(jì)數(shù)加1。

// a、同步問(wèn)題:輸出肯定為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、有限資源訪問(wèn)問(wèn)題: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語(yǔ)言接口形式的,所以其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語(yǔ)言的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. 常見(jiàn)的死鎖

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一樣的死鎖問(wèn)題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,615評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 176,606評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 63,044評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,826評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 55,227評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 42,447評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,992評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,807評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,001評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評(píng)論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,243評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 34,667評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 35,930評(píng)論 1 287
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,709評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,996評(píng)論 2 374

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