深入淺出GCD —— 所學(xué)即所用

CGD簡(jiǎn)介

什么是GCD

  • GCD的全稱是Grand Central Dispatch,可以把它翻譯為"牛逼的中樞調(diào)度器"。??
  • 純C語言,提供了很多強(qiáng)大的函數(shù)

GCD的優(yōu)勢(shì)

  • GCD是蘋果公司為解決多核的并行運(yùn)算而提出的解決方案
  • CGD會(huì)自動(dòng)利用更多的CPU內(nèi)容(如雙核、四核...)
  • GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)
  • 程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼

GCD中的概念和使用步驟

2個(gè)核心概念

任務(wù):

執(zhí)行的什么操作,用block代碼塊的形式創(chuàng)建

隊(duì)列:

是用來陳放任務(wù)的載體,負(fù)責(zé)調(diào)度任務(wù);隊(duì)列有兩種類型:

  • 串行隊(duì)列:一個(gè)接一個(gè)的調(diào)度任務(wù) (DISPATCH_QUEUE_SERIALdispatch_get_main_queue())
  • 并行隊(duì)列:可以同時(shí)調(diào)度多個(gè)任務(wù) (DISPATCH_QUEUE_CONCURRENTdispatch_get_global_queue(0,0))

使用步驟(2步)

定制任務(wù)

確定想要執(zhí)行的任務(wù)

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

  • GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出,放到對(duì)應(yīng)的線程中執(zhí)行
  • 任務(wù)的取出遵循隊(duì)列的FIFO原則:先進(jìn)先出

在這個(gè)過程中,我們需要做的就是:將任務(wù)以指定的方式(同步/異步)添加到隊(duì)列中,隊(duì)列就會(huì)按照我們指定的方式調(diào)度任務(wù)。

  • 同步:一個(gè)任務(wù)沒有結(jié)束,就不會(huì)執(zhí)行下一個(gè)任務(wù)
  • 異步:不用等當(dāng)前任務(wù)執(zhí)行完畢,就會(huì)去線程池開辟一個(gè)子線程,執(zhí)行下一個(gè)任務(wù)
  • 線程池:在GCD的底層有一個(gè)線程池,負(fù)責(zé)創(chuàng)建線程、分配線程、銷毀線程

我的理解

隊(duì)列、任務(wù)、線程池

如上圖,GCD主要由任務(wù)、隊(duì)列、執(zhí)行方式三個(gè)部分組成,又由于隊(duì)列類型(串行/并行)、執(zhí)行方式(同步/異步)的不同,可以兩兩組合,從而出現(xiàn)四種情況,后面會(huì)一一通過代碼來實(shí)現(xiàn)。這里主要說明的是:

  • 對(duì)于串行隊(duì)列
    當(dāng)任務(wù)執(zhí)行時(shí),必須要等當(dāng)前任務(wù)執(zhí)行完成后才能去隊(duì)列中拿下一個(gè)任務(wù),所以不管是同步執(zhí)行,還是異步執(zhí)行,都是按照順序執(zhí)行的

  • 對(duì)于并行隊(duì)列
    當(dāng)任務(wù)執(zhí)行時(shí),不用等當(dāng)前任務(wù)執(zhí)行完畢就可以去隊(duì)列拿下一個(gè)任務(wù),此時(shí)若是:

    • 同步執(zhí)行
      由于只有同步執(zhí)行不會(huì)去現(xiàn)成池拿新的線程,即使你拿到多個(gè)任務(wù),也只能在當(dāng)前線程排隊(duì)等待,等前一個(gè)任務(wù)執(zhí)行完畢后,才能執(zhí)行下一個(gè),所以還是按照順序執(zhí)行
    • 異步執(zhí)行
      如果在并行隊(duì)列異步執(zhí)行任務(wù)的話,首先會(huì)去隊(duì)列中拿到多個(gè)任務(wù),并且會(huì)去線程池中獲取子線程,所以會(huì)出現(xiàn)多個(gè)任務(wù)并發(fā)執(zhí)行的情況,這時(shí)候任務(wù)的執(zhí)行順序是不確定的,而我們用GCD大部分的場(chǎng)景都是這種情況

下面,通過代碼,來依次驗(yàn)證這些情況。

GCD實(shí)戰(zhàn)

創(chuàng)建隊(duì)列

所有的隊(duì)列都是dispatch_queue_t類型,常用創(chuàng)建隊(duì)列通常有三種方法,其中有兩種是系統(tǒng)提供的:

  • 主隊(duì)列
    通過 dispatch_get_main_queue()方法返回一個(gè)dispatch_queue_t對(duì)象,這是一個(gè)串行隊(duì)列,即該隊(duì)列中的所有任務(wù)都是順序執(zhí)行的。

  • 全局隊(duì)列
    這個(gè)隊(duì)列通過dispatch_get_global_queue(long identifier, unsigned long flags)函數(shù)獲取,對(duì)于這個(gè)函數(shù)有兩個(gè)參數(shù):

    • identifier
      這個(gè)參數(shù)是表示一個(gè)優(yōu)先級(jí)的,但是在iOS 8之前和之后其參數(shù)類型不太一樣
      iOS 8及之后版本表示的含義是:服務(wù)質(zhì)量,其類型如下:
     QOS_CLASS_USER_INTERACTIVE    用戶交互(希望線程快速被執(zhí)行,不要用好使的操作)
     QOS_CLASS_USER_INITIATED      用戶需要的(同樣不要使用耗時(shí)操作)
     QOS_CLASS_DEFAULT             默認(rèn)的(給系統(tǒng)來重置隊(duì)列的)
     QOS_CLASS_UTILITY             使用工具(用來做耗時(shí)操作)
     QOS_CLASS_BACKGROUND          后臺(tái)
     QOS_CLASS_UNSPECIFIED         沒有指定優(yōu)先級(jí)

而在iOS 8之前版本表示的含義是:調(diào)度的優(yōu)先級(jí),其類型如下:

     DISPATCH_QUEUE_PRIORITY_HIGH 2               高優(yōu)先級(jí)
     DISPATCH_QUEUE_PRIORITY_DEFAULT 0            默認(rèn)優(yōu)先級(jí)
     DISPATCH_QUEUE_PRIORITY_LOW (-2)             低優(yōu)先級(jí)
     DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后臺(tái)優(yōu)先級(jí)

提示

不要選擇BACKGROUND 優(yōu)先級(jí),服務(wù)質(zhì)量,因?yàn)榫€程執(zhí)行會(huì)慢到令人發(fā)指!!!體現(xiàn)在任務(wù)調(diào)度次數(shù)少,執(zhí)行時(shí)間短。通常基本都使用默認(rèn)的,填入0

    • flags: 是一個(gè)預(yù)留參數(shù),同常填0
  • 自己創(chuàng)建一個(gè)隊(duì)列
    GCD提供了一個(gè)可以自己創(chuàng)建隊(duì)列的函數(shù):dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t _Nullable attr#>)函數(shù),使用此函數(shù)可以創(chuàng)建一個(gè)自定義的隊(duì)列。
    • label參數(shù): 是一個(gè)隊(duì)列標(biāo)簽,即隊(duì)列名,通常給一個(gè)字符串
    • attr參數(shù): 是隊(duì)列屬性;系統(tǒng)提供了兩個(gè)宏:DISPATCH_QUEUE_SERIAL或者NULL因?yàn)檫@個(gè)宏就是NULL,表示串行隊(duì)列; DISPATCH_QUEUE_CONCURRENT:表示并行隊(duì)列。

添加任務(wù)

添加任務(wù):將任務(wù)以指定的執(zhí)行方式添加到隊(duì)列中去;它的方式有兩種:

  • 同步執(zhí)行:
    即只會(huì)在當(dāng)前線程中執(zhí)行任務(wù),無法去線程池中獲取新的線程
    /**
    *queue: 添加到的隊(duì)列
    *block:就放我們需要執(zhí)行的任務(wù)
    */
    dispatch_sync(dispatch_queue_t  _Nonnull queue, ^{
        //you todo 
    });
  • 異步執(zhí)行:
    即可以去線程池中獲取新的線程,其參數(shù)含義和同步執(zhí)行的相同,只是函數(shù)名換成了dispatch_async
    dispatch_async(dispatch_queue_t  _Nonnull queue, ^{
    
        //you todo
    }) 

介紹完了基本函數(shù)語法,下面來用代碼實(shí)現(xiàn)上述所說的4中情況。

串行隊(duì)列同步執(zhí)行

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    [self gcdBase1];
    
}

- (void)gcdBase1 {

    dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_SERIAL);
    for (NSInteger i = 0; i < 10; i ++) {
        dispatch_sync(queue, ^{
            NSLog(@"%ld ------ %@", (long)i,  [NSThread currentThread]);
        });
    }
}

我在touchesBegan方法中,串行隊(duì)列中以同步執(zhí)行的方法添加了10個(gè)NSLog任務(wù),當(dāng)點(diǎn)擊屏幕時(shí),控制臺(tái)獲得以下輸出:


2017-01-10 21:54:58.609 GCD-01[13124:1295897] 0 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.609 GCD-01[13124:1295897] 1 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.609 GCD-01[13124:1295897] 2 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.610 GCD-01[13124:1295897] 3 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.610 GCD-01[13124:1295897] 4 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.610 GCD-01[13124:1295897] 5 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.610 GCD-01[13124:1295897] 6 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.611 GCD-01[13124:1295897] 7 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.611 GCD-01[13124:1295897] 8 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
2017-01-10 21:54:58.611 GCD-01[13124:1295897] 9 ------ <NSThread: 0x600000079e00>{number = 1, name = main}

由結(jié)果可以看出,不會(huì)開啟線程,并且任務(wù)是順序執(zhí)行的,符合預(yù)期,并且無論你點(diǎn)擊多少次,結(jié)果都不會(huì)改變。

串行隊(duì)列異步執(zhí)行

這次我在touchesBegan方法中執(zhí)行以下任務(wù),在一個(gè)串行隊(duì)列中,添加10個(gè)異步任務(wù),并且在任務(wù)添加后,打印一句I am here,猜猜這次的打印結(jié)果會(huì)是怎么樣的?????

- (void)gcdBase2 {
    
    dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_SERIAL);
    for (NSInteger i = 0; i < 10; i ++) {
        dispatch_async(queue, ^{
            NSLog(@"%ld ------ %@", (long)i,  [NSThread currentThread]);
        });
    }
    NSLog(@"I am here");
}

在模擬器中點(diǎn)擊屏幕,獲得以下結(jié)果:

2017-01-10 22:06:01.703 GCD-01[13385:1322583] I am here <NSThread: 0x600000066a40>{number = 1, name = main}
2017-01-10 22:06:01.703 GCD-01[13385:1322734] 0 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.703 GCD-01[13385:1322734] 1 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.703 GCD-01[13385:1322734] 2 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.703 GCD-01[13385:1322734] 3 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.704 GCD-01[13385:1322734] 4 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.704 GCD-01[13385:1322734] 5 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.704 GCD-01[13385:1322734] 6 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.705 GCD-01[13385:1322734] 7 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.705 GCD-01[13385:1322734] 8 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
2017-01-10 22:06:01.705 GCD-01[13385:1322734] 9 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}

由以上結(jié)果可以看出,異步執(zhí)行,會(huì)創(chuàng)建新的線程去執(zhí)行任務(wù),在串行隊(duì)列中,由于每次只能拿一個(gè)任務(wù),所以任務(wù)是按照FIFO順序執(zhí)行的。這里的I am here位置不一定一直處于第一位,但是肯定是靠前的。但是我試了很多次,都是第一位,尷尬。。。

并行隊(duì)列同步執(zhí)行

我在touchesBegan方法中執(zhí)行以下任務(wù);將隊(duì)列類型換成DISPATCH_QUEUE_CONCURRENT,即并行隊(duì)列,然后添加同步任務(wù)

- (void)gcdBase3 {
    
    dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_CONCURRENT);
    for (NSInteger i = 0; i < 10; i ++) {
        dispatch_sync(queue, ^{
            NSLog(@"%ld ------ %@", (long)i,  [NSThread currentThread]);
        });
    }
    NSLog(@"I am here %@", [NSThread currentThread]);
}

點(diǎn)擊屏幕獲取打印:

2017-01-10 22:12:34.551 GCD-01[13537:1337073] 0 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.551 GCD-01[13537:1337073] 1 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.551 GCD-01[13537:1337073] 2 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.552 GCD-01[13537:1337073] 3 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.552 GCD-01[13537:1337073] 4 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.552 GCD-01[13537:1337073] 5 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.552 GCD-01[13537:1337073] 6 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.553 GCD-01[13537:1337073] 7 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.553 GCD-01[13537:1337073] 8 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.553 GCD-01[13537:1337073] 9 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
2017-01-10 22:12:34.553 GCD-01[13537:1337073] I am here <NSThread: 0x6180000764c0>{number = 1, name = main}

結(jié)果:由打印信息可以看出,同步任務(wù)不會(huì)創(chuàng)建新的線程,由于只有一個(gè)線程,即使是并行隊(duì)列可以同時(shí)獲取多個(gè)任務(wù),最終也會(huì)按照順序執(zhí)行,因?yàn)橹挥幸粭l線程。

并行隊(duì)列異步執(zhí)行

- (void)gcdBase4 {
    
    dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_CONCURRENT);
    for (NSInteger i = 0; i < 10; i ++) {
        dispatch_async(queue, ^{
            NSLog(@"%ld ------ %@", (long)i,  [NSThread currentThread]);
        });
    }
    NSLog(@"I am here %@", [NSThread currentThread]);
}

這次我多次點(diǎn)擊屏幕時(shí),獲得以下輸出:

2017-01-10 22:18:15.768 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
2017-01-10 22:18:15.768 GCD-01[13664:1350805] 0 ------ <NSThread: 0x618000070700>{number = 3, name = (null)}
2017-01-10 22:18:15.768 GCD-01[13664:1351252] 1 ------ <NSThread: 0x61800006bf80>{number = 4, name = (null)}
2017-01-10 22:18:15.768 GCD-01[13664:1351253] 2 ------ <NSThread: 0x610000073880>{number = 5, name = (null)}
2017-01-10 22:18:15.768 GCD-01[13664:1351254] 3 ------ <NSThread: 0x61800006afc0>{number = 6, name = (null)}
2017-01-10 22:18:15.768 GCD-01[13664:1351255] 4 ------ <NSThread: 0x6000000725c0>{number = 7, name = (null)}
2017-01-10 22:18:15.768 GCD-01[13664:1351256] 5 ------ <NSThread: 0x61800006a2c0>{number = 8, name = (null)}
2017-01-10 22:18:15.769 GCD-01[13664:1351257] 6 ------ <NSThread: 0x610000073540>{number = 9, name = (null)}
2017-01-10 22:18:15.769 GCD-01[13664:1351258] 7 ------ <NSThread: 0x618000070480>{number = 10, name = (null)}
2017-01-10 22:18:15.769 GCD-01[13664:1350805] 8 ------ <NSThread: 0x618000070700>{number = 3, name = (null)}
2017-01-10 22:18:15.769 GCD-01[13664:1351252] 9 ------ <NSThread: 0x61800006bf80>{number = 4, name = (null)}


2017-01-10 22:18:24.114 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
2017-01-10 22:18:24.115 GCD-01[13664:1351637] 1 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351259] 0 ------ <NSThread: 0x60800006e080>{number = 11, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351638] 2 ------ <NSThread: 0x6180000705c0>{number = 13, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351639] 3 ------ <NSThread: 0x61000006f240>{number = 14, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351641] 4 ------ <NSThread: 0x61800006c080>{number = 15, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351643] 5 ------ <NSThread: 0x600000071f80>{number = 16, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351640] 7 ------ <NSThread: 0x600000072ac0>{number = 18, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351644] 6 ------ <NSThread: 0x618000070500>{number = 17, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351637] 8 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
2017-01-10 22:18:24.115 GCD-01[13664:1351647] 9 ------ <NSThread: 0x610000073540>{number = 19, name = (null)}


2017-01-10 22:18:26.446 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
2017-01-10 22:18:26.446 GCD-01[13664:1351647] 0 ------ <NSThread: 0x610000073540>{number = 19, name = (null)}
2017-01-10 22:18:26.446 GCD-01[13664:1351637] 1 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
2017-01-10 22:18:26.446 GCD-01[13664:1351644] 2 ------ <NSThread: 0x618000070500>{number = 17, name = (null)}
2017-01-10 22:18:26.446 GCD-01[13664:1351640] 3 ------ <NSThread: 0x600000072ac0>{number = 18, name = (null)}
2017-01-10 22:18:26.447 GCD-01[13664:1351643] 4 ------ <NSThread: 0x600000071f80>{number = 16, name = (null)}
2017-01-10 22:18:26.447 GCD-01[13664:1351641] 5 ------ <NSThread: 0x61800006c080>{number = 15, name = (null)}
2017-01-10 22:18:26.447 GCD-01[13664:1351639] 6 ------ <NSThread: 0x61000006f240>{number = 14, name = (null)}
2017-01-10 22:18:26.447 GCD-01[13664:1351638] 7 ------ <NSThread: 0x6180000705c0>{number = 13, name = (null)}
2017-01-10 22:18:26.447 GCD-01[13664:1351259] 8 ------ <NSThread: 0x60800006e080>{number = 11, name = (null)}
2017-01-10 22:18:26.447 GCD-01[13664:1351646] 9 ------ <NSThread: 0x610000073d80>{number = 20, name = (null)}



2017-01-10 22:18:26.598 GCD-01[13664:1351646] 0 ------ <NSThread: 0x610000073d80>{number = 20, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
2017-01-10 22:18:26.598 GCD-01[13664:1351259] 1 ------ <NSThread: 0x60800006e080>{number = 11, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351638] 2 ------ <NSThread: 0x6180000705c0>{number = 13, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351639] 3 ------ <NSThread: 0x61000006f240>{number = 14, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351641] 4 ------ <NSThread: 0x61800006c080>{number = 15, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351643] 5 ------ <NSThread: 0x600000071f80>{number = 16, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351640] 6 ------ <NSThread: 0x600000072ac0>{number = 18, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351644] 7 ------ <NSThread: 0x618000070500>{number = 17, name = (null)}
2017-01-10 22:18:26.598 GCD-01[13664:1351637] 8 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
2017-01-10 22:18:26.599 GCD-01[13664:1351649] 9 ------ <NSThread: 0x61800006f680>{number = 21, name = (null)}



2017-01-10 22:18:26.734 GCD-01[13664:1351637] 1 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351649] 0 ------ <NSThread: 0x61800006f680>{number = 21, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351644] 2 ------ <NSThread: 0x618000070500>{number = 17, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
2017-01-10 22:18:26.734 GCD-01[13664:1351640] 3 ------ <NSThread: 0x600000072ac0>{number = 18, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351643] 4 ------ <NSThread: 0x600000071f80>{number = 16, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351641] 5 ------ <NSThread: 0x61800006c080>{number = 15, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351639] 6 ------ <NSThread: 0x61000006f240>{number = 14, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351638] 7 ------ <NSThread: 0x6180000705c0>{number = 13, name = (null)}
2017-01-10 22:18:26.734 GCD-01[13664:1351259] 8 ------ <NSThread: 0x60800006e080>{number = 11, name = (null)}
2017-01-10 22:18:26.735 GCD-01[13664:1351646] 9 ------ <NSThread: 0x610000073d80>{number = 20, name = (null)}

結(jié)果分析: 由多次點(diǎn)擊結(jié)果可以確定,異步任務(wù),開啟了新線程,并發(fā)執(zhí)行能夠拿到多個(gè)任務(wù),所以上面會(huì)出現(xiàn)任務(wù)執(zhí)行順序不一致,并且I am here的位置,也可以確定主線程沒有被影響。

一個(gè)模擬項(xiàng)目中的同步任務(wù)

在這里我模擬一個(gè)實(shí)際開發(fā)流程:比如在一個(gè)讀書的APP中,通常會(huì)出現(xiàn)付費(fèi)下載的情況,當(dāng)用戶在游客模式去瀏覽,發(fā)現(xiàn)到自己喜歡的書籍時(shí),去點(diǎn)擊下載,這時(shí)候就需要走 登錄 -> 支付 -> 下載,這就是一個(gè)同步任務(wù),這里有很多網(wǎng)絡(luò)請(qǐng)求,屬于耗時(shí)操作,通常我們都在子線程中去完成。在這個(gè)任務(wù)中,我們將設(shè)置一下流程,用戶必須先登錄,然后同時(shí)執(zhí)行支付和下載任務(wù)。對(duì)此,實(shí)現(xiàn)以下代碼:

- (void)gcdSyncTask {
    //創(chuàng)建一個(gè)并行隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_CONCURRENT);
    //同步添加 用戶登錄 任務(wù)
    dispatch_sync(queue, ^{
        NSLog(@"用戶登錄");
    });
    //異步添加 支付任務(wù) 可以在子線程中執(zhí)行
    dispatch_async(queue, ^{
        NSLog(@"支付任務(wù)");
    });
    //異步添加 下載任務(wù) 可以在子線程中執(zhí)行
    dispatch_async(queue, ^{
        NSLog(@"下載任務(wù)");
    });
}

當(dāng)點(diǎn)擊屏幕獲得以下打印:經(jīng)過多次點(diǎn)擊都可以發(fā)現(xiàn),用戶登錄永遠(yuǎn)都先執(zhí)行的,只有用戶登錄完成后,才會(huì)去執(zhí)行支付和下載任務(wù)。實(shí)際情況,任務(wù)的執(zhí)行會(huì)更加耗時(shí),那么支付和下載的完成順序也就會(huì)不確定

2017-01-10 22:50:53.816 GCD-01[14340:1423863] 用戶登錄
2017-01-10 22:50:53.816 GCD-01[14340:1423979] 支付任務(wù)
2017-01-10 22:50:53.816 GCD-01[14340:1423977] 下載任務(wù)

對(duì)模擬項(xiàng)目升級(jí)

在之前的項(xiàng)目中,所以的點(diǎn)擊添加任務(wù)都是在主線程中添加的,下面模擬一下在,子線程中添加上述的同步任務(wù)會(huì)出現(xiàn)什么情況:

- (void)gcdStrongSyncTask {
    //創(chuàng)建一個(gè)并行隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_CONCURRENT);
    
    //這里講之前的任務(wù) 放到task代碼快中
    void (^task)() = ^{
        
        for (NSInteger i = 0; i < 10; i ++) {
            dispatch_sync(queue, ^{
                NSLog(@"%ld ------ %@", (long)i,  [NSThread currentThread]);
            });
        }
        //同步添加 用戶登錄 任務(wù)
        dispatch_sync(queue, ^{
            NSLog(@"用戶登錄  %@", [NSThread currentThread]);
        });
        //異步添加 支付任務(wù) 可以在子線程中執(zhí)行
        dispatch_async(queue, ^{
            NSLog(@"支付任務(wù)   %@", [NSThread currentThread]);
        });
        //異步添加 下載任務(wù) 可以在子線程中執(zhí)行
        dispatch_async(queue, ^{
            NSLog(@"下載任務(wù)  %@", [NSThread currentThread]);
        });
    };
    //以異步任務(wù)的方式,將task在子線程中去執(zhí)行,從而完成在子線程中去執(zhí)行之前的任務(wù)
    dispatch_async(queue, task);
    NSLog(@" I come here    %@", [NSThread currentThread]);
}

以下結(jié)果是我多次點(diǎn)擊之后得到的,我選取其中有代表性的一段,可以發(fā)現(xiàn)兩點(diǎn):

  • 將之前的任務(wù)放在block中,從新以異步任務(wù)方式添加到隊(duì)列中,就會(huì)在子線程中執(zhí)行
  • "下載任務(wù)"會(huì)在"支付任務(wù)之前完成"
2017-01-10 23:05:00.053 GCD-01[14704:1458015]  I come here    <NSThread: 0x60000006e440>{number = 1, name = main}
2017-01-10 23:05:00.053 GCD-01[14704:1458400] 0 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.054 GCD-01[14704:1458400] 1 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.054 GCD-01[14704:1458400] 2 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.055 GCD-01[14704:1458400] 3 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.055 GCD-01[14704:1458400] 4 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.055 GCD-01[14704:1458400] 5 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.056 GCD-01[14704:1458400] 6 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.056 GCD-01[14704:1458400] 7 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.056 GCD-01[14704:1458400] 8 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.056 GCD-01[14704:1458400] 9 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.057 GCD-01[14704:1458400] 用戶登錄  <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.057 GCD-01[14704:1458400] 支付任務(wù)   <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.057 GCD-01[14704:1458089] 下載任務(wù)  <NSThread: 0x61800006dbc0>{number = 5, name = (null)}




2017-01-10 23:05:00.573 GCD-01[14704:1458015]  I come here    <NSThread: 0x60000006e440>{number = 1, name = main}
2017-01-10 23:05:00.573 GCD-01[14704:1458089] 0 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.574 GCD-01[14704:1458089] 1 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.574 GCD-01[14704:1458089] 2 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.574 GCD-01[14704:1458089] 3 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.575 GCD-01[14704:1458089] 4 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.575 GCD-01[14704:1458089] 5 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.575 GCD-01[14704:1458089] 6 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.575 GCD-01[14704:1458089] 7 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.576 GCD-01[14704:1458089] 8 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.576 GCD-01[14704:1458089] 9 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.576 GCD-01[14704:1458089] 用戶登錄  <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
2017-01-10 23:05:00.577 GCD-01[14704:1458400] 下載任務(wù)  <NSThread: 0x608000070600>{number = 6, name = (null)}
2017-01-10 23:05:00.577 GCD-01[14704:1458089] 支付任務(wù)   <NSThread: 0x61800006dbc0>{number = 5, name = (null)}

總結(jié)

對(duì)于GCD的基本使用,注意以下三點(diǎn):

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

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

  • 從哪說起呢? 單純講多線程編程真的不知道從哪下嘴。。 不如我直接引用一個(gè)最簡(jiǎn)單的問題,以這個(gè)作為切入點(diǎn)好了 在ma...
    Mr_Baymax閱讀 2,800評(píng)論 1 17
  • 1. GCD簡(jiǎn)介 什么是GCD呢?我們先來看看百度百科的解釋簡(jiǎn)單了解下概念 引自百度百科:Grand Centra...
    千尋_544f閱讀 387評(píng)論 0 0
  • 什么是GCD 全稱是GrandCentral Dispatch,可譯為“牛逼的中樞調(diào)度器” 純C語言,提供了非常多...
    JonesCxy閱讀 305評(píng)論 0 1
  • 目錄(GCD): 關(guān)鍵詞 混淆點(diǎn) 場(chǎng)景應(yīng)用 總結(jié) 1. 關(guān)鍵詞 線程概念: 獨(dú)立執(zhí)行的代碼段,一個(gè)線程同時(shí)間只能執(zhí)...
    Ryan___閱讀 1,282評(píng)論 0 3
  • 《C陷阱與缺陷》 Andrew Koenig 讀書筆記 1.1 =和== 編寫時(shí)建議:將變量放到==的后方,編譯器...
    rfish閱讀 742評(píng)論 2 9