【譯】GCD并發(fā)隊(duì)列(GCD Concurrent Queues)

這是GCD介紹的第三篇文章。

如果說(shuō)串行隊(duì)列可以很好的替代互斥鎖症昏,那么并發(fā)隊(duì)列就可以很好的替代多線(xiàn)程。

并發(fā)隊(duì)列允許你入隊(duì)多個(gè)block议经,并且它們的執(zhí)行不需要等待前一個(gè)block執(zhí)行結(jié)束才開(kāi)始横堡。

運(yùn)行下面這段程序幾次:

#import <Foundation/Foundation.h>

void print(int number) {
    for (int count = 0; count < 10; ++count) {
        NSLog(@"%d", number);
    }
}

int main(int argc, const char * argv[]) {
    dispatch_queue_t queue = dispatch_queue_create("My concurrent queue", DISPATCH_QUEUE_CONCURRENT);

    @autoreleasepool {
        for (int index = 0; index < 5; ++index) {
            dispatch_async(queue, ^{
                print(index);
            });
        }
    }
    dispatch_main();
    return 0;
}

dispatch_async()告訴GCD去入隊(duì)一個(gè)block埋市,但是不用等待這個(gè)block執(zhí)行完畢才繼續(xù)接下來(lái)的操作。這就允許我們可以快速一次把5個(gè)block入隊(duì)到我們剛才創(chuàng)建的隊(duì)列中命贴。

當(dāng)?shù)谝粋€(gè)block被入隊(duì)時(shí)道宅,隊(duì)列還是空的,和串行隊(duì)列一樣這個(gè)block會(huì)立即開(kāi)始執(zhí)行胸蛛。然而污茵,第二個(gè)block入隊(duì)之后也會(huì)立即執(zhí)行,即使第一個(gè)block還沒(méi)執(zhí)行完畢胚泌,第三個(gè)省咨,第四個(gè),第五個(gè)block也是如此玷室,它們會(huì)一起開(kāi)始執(zhí)行零蓉。

每一個(gè)入隊(duì)的blcok在創(chuàng)建的時(shí)候獲取了一個(gè)index值,然后將這個(gè)值打印10次穷缤。程序的輸出結(jié)果如你所料嗎敌蜂?為什么每次運(yùn)行輸出的結(jié)果都不一樣?

<p>

如果我們把并發(fā)隊(duì)列換成串行隊(duì)列結(jié)果會(huì)怎么樣呢津肛?試試吧章喉!把DISPATCH_QUEUE_CONCURRENT換成DISPATCH_QUEUE_SERIAL,然后再運(yùn)行看看身坐。

用隊(duì)列秸脱,不用線(xiàn)程

你可能沒(méi)注意到,上面的程序在完全不用pthread_create()NSThread的情況下部蛇,輕松得創(chuàng)建的5個(gè)線(xiàn)程摊唇。因?yàn)椴l(fā)隊(duì)列中的每一個(gè)block需要同時(shí)運(yùn)行,所以GCD會(huì)為每一個(gè)block自動(dòng)的創(chuàng)建(或搶占)一個(gè)線(xiàn)程來(lái)執(zhí)行它們涯鲁,一旦有某一個(gè)block執(zhí)行完畢巷查,它對(duì)應(yīng)的線(xiàn)程就會(huì)被銷(xiāo)毀或者放回線(xiàn)程池中。使用GCD抹腿,你只需要關(guān)心隊(duì)列岛请,讓GCD庫(kù)去關(guān)心線(xiàn)程的事。

雖然你不用手動(dòng)去管理線(xiàn)程警绩,但是你還是不能忽略線(xiàn)程的限制崇败。如果你的入隊(duì)的block數(shù)量超過(guò)了進(jìn)程中的可用線(xiàn)程數(shù),你的程序?qū)?huì)終止運(yùn)行肩祥,而這通常是沒(méi)有預(yù)警的僚匆。

障礙(Barriers)

這時(shí)自然會(huì)有一個(gè)問(wèn)題產(chǎn)生:既然并發(fā)隊(duì)列允許所有的block一起執(zhí)行微渠,那為什么它還叫“隊(duì)列”呢?它不是更像一個(gè)可以加入并行執(zhí)行block的堆嗎咧擂?

當(dāng)你考慮到障礙(Barriers)的時(shí)候,并發(fā)隊(duì)列看起來(lái)就像一個(gè)”隊(duì)列“了大咱。加入障礙(Barriers)之后歌溉,你通過(guò)dispatch_barrier_sync()dispatch_barrier_async()入隊(duì)的block會(huì)發(fā)生一些有趣的事:這個(gè)障礙block(譯者注:障礙也是以block的形式加入隊(duì)列中的)將會(huì)被入隊(duì)凿叠,但是不會(huì)立即執(zhí)行,而是會(huì)等到在它之前入隊(duì)的隊(duì)列執(zhí)行完畢之后才會(huì)開(kāi)始執(zhí)行贸桶。另外,所有在障礙block之后入隊(duì)的block會(huì)等到障礙block本身執(zhí)行完畢之后才會(huì)執(zhí)行桌肴』噬福可以把障礙block看做是一個(gè)“瓶頸”,在一系列并行執(zhí)行的操作中坠七,強(qiáng)制串行執(zhí)行水醋。

下面這段程序展示了的屏障(Barriers)的作用:

#import <Foundation/Foundation.h>

void print(int number) {
    for (int count = 0; count < 10; ++count) {
        NSLog(@"%d", number);
    }
}

int main(int argc, const char * argv[]) {
    dispatch_queue_t queue = dispatch_queue_create("My concurrent queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_suspend(queue); // Suspend the queue so blocks are enqueued, but not executed

    @autoreleasepool {
        // Enqueue five blocks
        for (int index = 0; index < 5; ++index) {
            dispatch_async(queue, ^{
                print(index);
            });
        }

        // Enqueue a barrier
        dispatch_barrier_async(queue, ^{
            NSLog(@"--- This is a barrier ---");
        });

        // Enqueue five more blocks
        for (int index = 5; index < 10; ++index) {
            dispatch_async(queue, ^{
                print(index);
            });
        }
    }
    dispatch_resume(queue); // Go!
    dispatch_main();
    return 0;
}

運(yùn)行一下這段程序,注意到在障礙之前彪置,只有index為0到4的block執(zhí)行了拄踪,而在障礙之后,只有index為5到9的block執(zhí)行了拳魁。但是在障礙前后的單獨(dú)一邊惶桐,5個(gè)block是同時(shí)執(zhí)行的。

讀寫(xiě)鎖(Readers-Writer Locks)

在我之前的文章中潘懊,我介紹了如何使用串行隊(duì)列保護(hù)數(shù)據(jù)變量以防止競(jìng)態(tài)條件的發(fā)生姚糊。這種方法使得同一時(shí)間只有一個(gè)線(xiàn)程訪(fǎng)問(wèn)變量,保證了操作的原子性授舟。

但是實(shí)際上救恨,在同步執(zhí)行的操作中我們不用做任何事來(lái)防止靜競(jìng)態(tài)條件的發(fā)生,我們需要做的是防止數(shù)據(jù)被異步的改變岂却,而多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)而不改變同一份數(shù)據(jù)是被允許的(從性能的角度講忿薇,這也應(yīng)該是較好的做法)。

這時(shí)我們就需要一個(gè)讀寫(xiě)鎖(readers-writer lock)躏哩,它的功能是允許對(duì)數(shù)據(jù)讀的操作可以是并行同時(shí)發(fā)生的署浩,但是寫(xiě)的操作必須是串行的。

使用異步隊(duì)列和障礙扫尺,我們可以輕松的實(shí)現(xiàn)一個(gè)讀寫(xiě)鎖筋栋,這里有一個(gè)例子:

#import <Foundation/Foundation.h>

dispatch_queue_t queue;

NSString *he = @"Luke";
NSString *she = @"Megan";

void printAndRepeat() {
    NSLog(@"%@ likes %@!", he, she);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), queue, 
    // This block is dispatch_async'd to the concurrent queue after 1 second
    ^{
        printAndRepeat();
    });
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        queue = dispatch_queue_create("Reader-writer queue", DISPATCH_QUEUE_CONCURRENT);

        // Create readers
        for (int index = 0; index < 5; ++index) {
            dispatch_async(queue, ^{
                printAndRepeat();
            });
        }

        // Change the variables after 5 seconds
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), 
        // This block is enqueued onto the main queue after 5 seconds.
        ^{
            dispatch_barrier_async(queue, ^{
                he = @"Don";
                she = @"Alice";
            });
        });
    }
    dispatch_main();
    return 0;
}

你目前可以先忽略dispatch_after()的調(diào)用,它只是簡(jiǎn)單的告訴GCD在一段時(shí)間后入隊(duì)一個(gè)block正驻。

在這個(gè)例子中弊攘,障礙block(把heshe改成“Don”和“Alice”)保證了對(duì)數(shù)據(jù)修改這一操作的原子性抢腐。因?yàn)檎系K隊(duì)列運(yùn)行時(shí)不會(huì)被其他block中斷或打擾,所以你永遠(yuǎn)不會(huì)看見(jiàn)“Luke likes Alice!”打印在你的控制臺(tái)襟交。

恭喜迈倍!你現(xiàn)在對(duì)并發(fā)隊(duì)列有了一定的了解,知道了如何用它取代多線(xiàn)程捣域,以及如何利用它創(chuàng)建一個(gè)高效的讀寫(xiě)鎖啼染。在下一篇文章中,我們將研究一下全局并發(fā)隊(duì)列和目標(biāo)隊(duì)列焕梅,下次見(jiàn)迹鹅!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市贞言,隨后出現(xiàn)的幾起案子斜棚,更是在濱河造成了極大的恐慌,老刑警劉巖该窗,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弟蚀,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡挪捕,警方通過(guò)查閱死者的電腦和手機(jī)粗梭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)级零,“玉大人断医,你說(shuō)我怎么就攤上這事∽嗉停” “怎么了鉴嗤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)序调。 經(jīng)常有香客問(wèn)我醉锅,道長(zhǎng),這世上最難降的妖魔是什么发绢? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任硬耍,我火速辦了婚禮,結(jié)果婚禮上边酒,老公的妹妹穿的比我還像新娘经柴。我一直安慰自己,他們只是感情好墩朦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布坯认。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪牛哺。 梳的紋絲不亂的頭發(fā)上陋气,一...
    開(kāi)封第一講書(shū)人閱讀 51,155評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音引润,去河邊找鬼巩趁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛淳附,可吹牛的內(nèi)容都是我干的晶渠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼燃观,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了便瑟?” 一聲冷哼從身側(cè)響起缆毁,我...
    開(kāi)封第一講書(shū)人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎到涂,沒(méi)想到半個(gè)月后脊框,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡践啄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年浇雹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屿讽。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昭灵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伐谈,到底是詐尸還是另有隱情烂完,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布诵棵,位于F島的核電站抠蚣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏履澳。R本人自食惡果不足惜嘶窄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望距贷。 院中可真熱鬧柄冲,春花似錦、人聲如沸储耐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至长赞,卻和暖如春晦攒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背得哆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工脯颜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人贩据。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓栋操,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親饱亮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子矾芙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

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

  • GCD 深入理解:第一部分 什么是 GCD GCD 是 libdispatch 的市場(chǎng)名稱(chēng),而 libdispat...
    willphonez閱讀 623評(píng)論 0 2
  • 雖然 GCD 已經(jīng)出現(xiàn)過(guò)一段時(shí)間了近上,但不是每個(gè)人都明了其主要內(nèi)容剔宪。這是可以理解的;并發(fā)一直很棘手壹无,而 GCD 是基...
    隨風(fēng)飄蕩的小逗逼閱讀 1,330評(píng)論 0 2
  • 本文翻譯自 http://www.raywenderlich.com/60749/grand-central-di...
    skogt閱讀 1,230評(píng)論 2 10
  • 第一次看東野圭吾的書(shū)葱绒。我本來(lái)并沒(méi)有想把這本書(shū)看完的,但它有種讓人不斷往下讀的魔力斗锭,當(dāng)我把這本書(shū)看完后才發(fā)現(xiàn)地淀,作者對(duì)...
    Gigi_qin閱讀 387評(píng)論 0 0
  • Hello! 艾瑞巴蒂前硫! 偷偷問(wèn)你們胞得, 禮物準(zhǔn)備好了嗎? 驚喜準(zhǔn)備好了嗎屹电? 對(duì)象準(zhǔn)備好了嗎阶剑? ———————— Y...
    最匯吃_閱讀 293評(píng)論 0 0