GCD與Dispatch Queue


什么是GCD與Dispatch Queue谱醇?

GCD:是Grand Central Dispatch縮寫,是Apple公司封裝的一個用于進(jìn)行并發(fā)程序設(shè)計(jì)的一個API窄锅,它以C語言為基礎(chǔ)伶丐,將線程的操作抽象為對隊(duì)列的操作。
??Dispatch Queue:Dispatch Queue是GCD中用到的隊(duì)列類型锐朴,有串行隊(duì)列、并發(fā)隊(duì)列蔼囊、主隊(duì)列等焚志。

GCD有什么用?

在進(jìn)行iOS開發(fā)過程畏鼓,我們經(jīng)常需要對多個任務(wù)進(jìn)行操作酱酬,這時就需要用到線程,iOS開發(fā)中對線程的操作多種多樣云矫,有PThreads膳沽、NSThread、NSOperation以及GCD让禀。
??Pthread:是類Unix系統(tǒng)中用來對線程進(jìn)行操作的一組C語言寫的API挑社,可以在Unix、Mac OX S巡揍、Linux中使用痛阻,學(xué)習(xí)Linux下的C語言多線程會用到這個。
??NSThread:是Apple對Pthread的抽象腮敌,NSThread對Pthread的抽象程度較低阱当,在進(jìn)行多線程開發(fā)時,依然需要進(jìn)行對線程進(jìn)行創(chuàng)建缀皱、使用斗这、銷毀等操作。
??NSOperation Queue:Apple基于GCD封裝的一種基于Queue操作的并發(fā)處理技術(shù)啤斗。
??GCD:基于C語言封裝的一個對任務(wù)進(jìn)行操作的并發(fā)處理技術(shù)表箭。

??在封裝的三種API中,Apple公司最為推崇GCD與NSOperation Queue钮莲,因?yàn)檫@兩種方式讓開發(fā)者的注意力轉(zhuǎn)向如何對并發(fā)程序進(jìn)行設(shè)計(jì)免钻,不用關(guān)注線程到底是怎樣執(zhí)行任務(wù)的,提高了開發(fā)的效率崔拥。關(guān)于并發(fā)程序的設(shè)計(jì)可以查看官方文檔Concurrency and Application Design极舔,里面有對GCD、NSOperation Queue以及其它并發(fā)技術(shù)的詳細(xì)講解链瓦。

需要了解的知識

關(guān)于GCD拆魏,需要串行盯桦、并發(fā)和同步以及異步這些知識。在GCD中渤刃,串行和并發(fā)是用來形容Dispatch Queue的拥峦。
??并發(fā)(concurrent):在操作系統(tǒng)中,同一時間段卖子,有多個程序同時在處理器上執(zhí)行略号,就像這些程序在同時進(jìn)行一樣。在單一CPU的機(jī)器上洋闽,并發(fā)技術(shù)基于時間片技術(shù)玄柠,而在多核機(jī)器上,并發(fā)技術(shù)基于多核技術(shù)的發(fā)展诫舅。在GCD中并發(fā)隊(duì)列中的任務(wù)可以不用等前一個任務(wù)執(zhí)行完就能執(zhí)行羽利。
??串行(serial):既然并發(fā)是多個程序看起來在同時進(jìn)行,那么串行就是多個程序排隊(duì)一個一個執(zhí)行骚勘。在GCD串行隊(duì)列中铐伴,隊(duì)列中的任務(wù)需要等出隊(duì)的任務(wù)執(zhí)行完后才能接著執(zhí)行。

在GCD中俏讹,同步與異步是指任務(wù)之間的制約關(guān)系的。
??同步:同步在操作系統(tǒng)中是指進(jìn)程之間的制約關(guān)系畜吊。一個進(jìn)程要等另一個進(jìn)程執(zhí)行完成后才能執(zhí)行泽疆,而不是同時執(zhí)行,是進(jìn)程之間的執(zhí)行次序的管理玲献。例如殉疼,進(jìn)程B需要用到進(jìn)程A的結(jié)果才能執(zhí)行,因此系統(tǒng)會先執(zhí)行A然后在執(zhí)行B捌年,進(jìn)程A與B的關(guān)系就是同步瓢娜。同理,在GCD中礼预,具有同步關(guān)系的兩個任務(wù)眠砾,一個需要等另一個執(zhí)行完后才能執(zhí)行,某種程度上托酸,同步的效果與串行相似褒颈。
??異步:與同步的意思相反,既然同步是一個接一個執(zhí)行励堡,那么異步就是后一個任務(wù)不必等前一個任務(wù)執(zhí)行完谷丸,即任務(wù)A執(zhí)行的結(jié)果并不影響任務(wù)B,任務(wù)A與任務(wù)B不相關(guān)应结,沒有確定的先后順序刨疼。

Dispatch Queue類型

串行隊(duì)列:任務(wù)按被加入到隊(duì)列中的順序依次執(zhí)行,每次只從隊(duì)中取出一個任務(wù)執(zhí)行。

串行隊(duì)列

并發(fā)隊(duì)列:同時執(zhí)行一個或多個任務(wù)揩慕,但任務(wù)仍然是按它們被添加到隊(duì)列中的順序開始的亭畜。

并發(fā)隊(duì)列

主隊(duì)列:是一個全局串行隊(duì)列,它在應(yīng)用程序的主線程上執(zhí)行任務(wù)漩绵。因?yàn)樗\(yùn)行在您的應(yīng)用程序的主線程上贱案,主隊(duì)列經(jīng)常被用來作為應(yīng)用程序的一個關(guān)鍵的同步點(diǎn)。

創(chuàng)建Dispatch Queue

  1. 串行隊(duì)列

     dispatch_queue_t serial = dispatch_queue_create("serial_queue", 0);
    

參數(shù)一是隊(duì)列名止吐,用于Debug時進(jìn)行跟蹤宝踪,參數(shù)二是隊(duì)列屬性,創(chuàng)建串行隊(duì)列碍扔,默認(rèn)為0瘩燥,或者設(shè)為DISPATCH_QUEUE_SERIAL。

  1. 并發(fā)隊(duì)列
    • 自定義并發(fā)隊(duì)列

        dispatch_queue_t concur = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
      

與創(chuàng)建串行隊(duì)列一樣不同,不過參數(shù)二為DISPATCH_QUEUE_CONCURRENT厉膀。
* 全局并發(fā)隊(duì)列

        dispatch_queue_t concur = dispatch_get_global_queue(ISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

參數(shù)一是全局并發(fā)隊(duì)列的優(yōu)先級,參數(shù)二為未來新特性預(yù)留二拐,默認(rèn)為0即可服鹅。全局并發(fā)隊(duì)列分別有四個優(yōu)先級:

        DISPATCH_QUEUE_PRIORITY_HIGHT,
        DISPATCH_QUEUE_PRIORITY_DEFAULT,
        DISPATCH_QUEUE_PRIORITY_LOW,
        DISPATCH_QUEUE_PRIORITY_BACKGROUND  
  1. 主隊(duì)列

     dispatch_queue_t main = dispatch_get_main_queue();
    

隊(duì)列與線程的關(guān)系

objc上一篇名為Concurrent Programming: APIs and Challenges的文章中的一張圖很好的描述了隊(duì)列與線程的關(guān)系。

隊(duì)列與線程池

??主隊(duì)列中的任務(wù)都是在主線程中執(zhí)行的百新,自定義的串行隊(duì)列和并發(fā)隊(duì)列中的任務(wù)企软,最終會流入系統(tǒng)的全局隊(duì)列,系統(tǒng)會從線程池中分配相應(yīng)的隊(duì)列去執(zhí)行饭望。
隊(duì)列與線程的關(guān)系

將任務(wù)添加到隊(duì)列

在GCD中仗哨,任務(wù)通常是以block的形式添加到隊(duì)列當(dāng)中的,任務(wù)的添加有兩種方式铅辞,dispatch_sync(同步)和dispatch_async(異步)厌漂,這兩種添加方式?jīng)Q定了隊(duì)列中的任務(wù)之間的制約關(guān)系。

  1. dispatch_sync
    ??將block提交到隊(duì)列中斟珊,并阻塞當(dāng)前隊(duì)列苇倡,直到block執(zhí)行結(jié)束才回返回到當(dāng)前隊(duì)列,提交的任務(wù)之間以同步方式執(zhí)行倍宾。需要注意的是雏节,Apple關(guān)于dispatch_sync的API文檔中有這樣一句話,As an optimization, this function invokes the block on the current thread when possible高职。作為優(yōu)化钩乍,dispatch_sync阻塞當(dāng)前隊(duì)列后,系統(tǒng)會讓當(dāng)前線程執(zhí)行block語句怔锌。

  2. dispatch_async
    ??不等提交到隊(duì)列的block開始執(zhí)行就立即返回到當(dāng)前線程寥粹。該函數(shù)將block提交到隊(duì)列后变过,系統(tǒng)會從線程池中分配線程依次執(zhí)行隊(duì)列中的block(并發(fā)隊(duì)列分配的線程個數(shù)由系統(tǒng)的狀態(tài)決定),這樣就實(shí)現(xiàn)了異步的執(zhí)行方式涝涤。

代碼示例:

dispatch_queue_t serial = dispatch_queue_create("serial_queue", 0);
dispatch_queue_t concur = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(serial, ^{
    NSLog(@"%@-sync",[NSThread currentThread]);
});
dispatch_async(serial, ^{
    NSLog(@"%@-async",[NSThread currentThread])
});
dispatch_main();

以上是對于GCD的基本使用媚狰,有關(guān)GCD的具體使用可以參考官方文檔Dispatch Queue以及官方API參考Dispatch

通過例子我們來看一下,串行隊(duì)列與并發(fā)隊(duì)列的線程數(shù)目阔拳。
代碼示例:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...

        NSLog(@"Hello, World!");

        dispatch_queue_t serial = dispatch_queue_create("serial_queue", 0);
        dispatch_queue_t concur = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
    
        for(int i = 0; i < 5; i++)
        {
            dispatch_sync(serial, ^{
                NSLog(@"serial-%@-sync",[NSThread currentThread]);
            });
            dispatch_async(serial, ^{
                NSLog(@"serial-%@-async",[NSThread currentThread]);
            });
        }
    
        for(int i = 0; i < 5; i++)
        {
            dispatch_sync(concur, ^{
                NSLog(@"concurrent-%@-sync",[NSThread currentThread]);
            });
            dispatch_async(concur, ^{
                NSLog(@"concurrent-%@-async",[NSThread currentThread]);
            
            });
        }
        dispatch_main();
    }
    return 0;
}

為了結(jié)果直觀顯示崭孤,將不測的部分注釋后運(yùn)行,結(jié)果如下:

  1. 串行隊(duì)列同步添加

     2016-08-14 18:20:30.313 GCD[798:89819] Hello, World!
     2016-08-14 18:20:30.315 GCD[798:89819] serial-<NSThread: 0x100602c80>{number = 1, name = main}-sync
     2016-08-14 18:20:30.315 GCD[798:89819] serial-<NSThread: 0x100602c80>{number = 1, name = main}-sync
     2016-08-14 18:20:30.315 GCD[798:89819] serial-<NSThread: 0x100602c80>{number = 1, name = main}-sync
     2016-08-14 18:20:30.315 GCD[798:89819] serial-<NSThread: 0x100602c80>{number = 1, name = main}-sync
     2016-08-14 18:20:30.315 GCD[798:89819] serial-<NSThread: 0x100602c80>{number = 1, name = main}-sync
     Program ended with exit code: 0
    
  2. 串行隊(duì)列異步添加

     2016-08-14 18:24:36.657 GCD[830:92007] Hello, World!
     2016-08-14 18:24:36.659 GCD[830:92026] serial-<NSThread: 0x100203d20>{number = 2, name = (null)}-async
     2016-08-14 18:24:36.659 GCD[830:92026] serial-<NSThread: 0x100203d20>{number = 2, name = (null)}-async
     2016-08-14 18:24:36.659 GCD[830:92026] serial-<NSThread: 0x100203d20>{number = 2, name = (null)}-async
     2016-08-14 18:24:36.659 GCD[830:92026] serial-<NSThread: 0x100203d20>{number = 2, name = (null)}-async
     2016-08-14 18:24:36.659 GCD[830:92026] serial-<NSThread: 0x100203d20>{number = 2, name = (null)}-async
    
  3. 并發(fā)隊(duì)列同步添加

     2016-08-14 18:28:53.662 GCD[862:94207] Hello, World!
     2016-08-14 18:28:53.663 GCD[862:94207] concurrent-<NSThread: 0x100502c80>{number = 1, name = main}-sync
     2016-08-14 18:28:53.663 GCD[862:94207] concurrent-<NSThread: 0x100502c80>{number = 1, name = main}-sync
     2016-08-14 18:28:53.663 GCD[862:94207] concurrent-<NSThread: 0x100502c80>{number = 1, name = main}-sync
     2016-08-14 18:28:53.663 GCD[862:94207] concurrent-<NSThread: 0x100502c80>{number = 1, name = main}-sync
     2016-08-14 18:28:53.663 GCD[862:94207] concurrent-<NSThread: 0x100502c80>{number = 1, name = main}-sync
    
  4. 并發(fā)隊(duì)列異步添加

     2016-08-14 18:29:39.743 GCD[872:94931] concurrent-<NSThread: 0x100500050>{number = 3, name = (null)}-async
     2016-08-14 18:29:39.743 GCD[872:94932] concurrent-<NSThread: 0x100700560>{number = 4, name = (null)}-async
     2016-08-14 18:29:39.743 GCD[872:94933] concurrent-<NSThread: 0x1007006f0>{number = 5, name = (null)}-async
     2016-08-14 18:29:39.743 GCD[872:94930] concurrent-<NSThread: 0x100403380>{number = 2, name = (null)}-async
     2016-08-14 18:29:39.743 GCD[872:94928] concurrent-<NSThread: 0x100500550>{number = 6, name = (null)}-async
    

對比以上結(jié)果糊肠,可以得出結(jié)論:

  1. 隊(duì)列的類型決定所需要的線程的個數(shù)辨宠。串行隊(duì)列和并發(fā)隊(duì)列執(zhí)行任務(wù)所需要的線程個數(shù)不同,串行隊(duì)列只需要一個線程货裹,并發(fā)隊(duì)列需要多個線程(線程個數(shù)由系統(tǒng)決定)嗤形。
  2. dispatch_sync和dispatch_async決定了是否開啟線程執(zhí)行隊(duì)列中的任務(wù)。dispatch_sync在當(dāng)前線程中執(zhí)行隊(duì)列中的任務(wù)弧圆,dispatch_async從線程池中至少獲取一個線程來赋兵,保證異步執(zhí)行。
  3. 無論是串行隊(duì)列搔预,還是并發(fā)隊(duì)列霹期,使用dispatch_sync添加任務(wù)后,任務(wù)都是在當(dāng)前線程中執(zhí)行拯田,這是由于Apple公司優(yōu)化的結(jié)果经伙。
  4. 對于串行隊(duì)列使用dispatch_async,系統(tǒng)會新開一個線程來執(zhí)行隊(duì)列中的任務(wù)勿锅,并沒有開多個線程,可以理解為因?yàn)槿蝿?wù)一次只執(zhí)行一個枣氧,若每個任務(wù)都創(chuàng)建一個線程運(yùn)行溢十,開銷巨大,并不劃算达吞,干脆使用同一線程依次執(zhí)行所有任務(wù)张弛。
  5. 對于并發(fā)隊(duì)列使用dispatch_async,由于隊(duì)列中的任務(wù)都需要并發(fā)運(yùn)行酪劫,如果只開一個線程吞鸭,則隊(duì)列中的任務(wù)之間相當(dāng)于串行執(zhí)行,所以開了多個線程覆糟,至于線程開多少個這由系統(tǒng)決定刻剥,例如將for循環(huán)的次數(shù)改為100,可以看到有多個任務(wù)使用同一線程滩字。

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末御吞,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子漓藕,更是在濱河造成了極大的恐慌陶珠,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件享钞,死亡現(xiàn)場離奇詭異揍诽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)栗竖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門暑脆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人划滋,你說我怎么就攤上這事饵筑。” “怎么了处坪?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵根资,是天一觀的道長。 經(jīng)常有香客問我同窘,道長玄帕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任想邦,我火速辦了婚禮裤纹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘丧没。我一直安慰自己鹰椒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布呕童。 她就那樣靜靜地躺著漆际,像睡著了一般。 火紅的嫁衣襯著肌膚如雪夺饲。 梳的紋絲不亂的頭發(fā)上奸汇,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機(jī)與錄音往声,去河邊找鬼擂找。 笑死,一個胖子當(dāng)著我的面吹牛浩销,可吹牛的內(nèi)容都是我干的贯涎。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼撼嗓,長吁一口氣:“原來是場噩夢啊……” “哼柬采!你這毒婦竟也來了欢唾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤粉捻,失蹤者是張志新(化名)和其女友劉穎礁遣,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肩刃,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡祟霍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了盈包。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沸呐。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖呢燥,靈堂內(nèi)的尸體忽然破棺而出崭添,到底是詐尸還是另有隱情,我是刑警寧澤叛氨,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布呼渣,位于F島的核電站,受9級特大地震影響寞埠,放射性物質(zhì)發(fā)生泄漏屁置。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一仁连、第九天 我趴在偏房一處隱蔽的房頂上張望蓝角。 院中可真熱鬧,春花似錦饭冬、人聲如沸使鹅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽并徘。三九已至,卻和暖如春扰魂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蕴茴。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工劝评, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人倦淀。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓蒋畜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親撞叽。 傳聞我的和親對象是個殘疾皇子姻成,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

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