iOS開(kāi)發(fā)之多線程編程總結(jié)(二)

背景

擔(dān)心了兩周的我終于輪到去醫(yī)院做胃鏡檢查了!去的時(shí)候我都想好了最壞的可能(胃癌),之前在網(wǎng)上查的癥狀都很相似。最后檢查結(jié)果出來(lái)終于安心了,診斷結(jié)果:慢性非萎縮性胃炎(胃竇為主)

我是一個(gè)心里素質(zhì)不過(guò)關(guān)的人,所以說(shuō)對(duì)待問(wèn)題的時(shí)候可能會(huì)有一種悲觀的想法。朋友說(shuō)我本來(lái)可能沒(méi)病都被自己嚇出病了,這是一個(gè)心態(tài)問(wèn)題。

你們可能問(wèn)我做胃鏡什么感覺(jué)?我只能告訴你一個(gè)字:真爽,具體只能自己去感受。

自己眼中的自己.jpg

保持樂(lè)觀的心態(tài)

看完下面的笑話就要開(kāi)始我們的裝逼之旅了_

  1. 一個(gè)大學(xué)生去公司實(shí)習(xí),老板讓他先從掃地開(kāi)始。大學(xué)生:“我可是大學(xué)生哎……”老板:“哦,對(duì)了,我差點(diǎn)忘了你是大學(xué)生,來(lái)來(lái)來(lái),我教你怎么掃地”
  1. 一天,老師讓同學(xué)們寫(xiě)作文,題目是 《我的理想》。
    小明在作文里寫(xiě)道:我長(zhǎng)大了要去搶銀行,然后把錢(qián)分給窮苦老百姓。
    第二天老師改完了,寫(xiě)給小明的評(píng)語(yǔ)是這樣的:很不錯(cuò)的理想,分錢(qián)的時(shí)候不要忘了老師,但你要注意你的同桌,他說(shuō)他長(zhǎng)大了要去當(dāng)警察。

GCD基本介紹

  • GCD(Grand Central Dispatch)iOS 4.0開(kāi)始引入的新多線程編程功能,
  • GCD(Grand Central Dispatch)是異步執(zhí)行任務(wù)的技術(shù)之一。一般將應(yīng)用程序中記述的線程管理用的代碼在系統(tǒng)級(jí)中實(shí)現(xiàn)。開(kāi)發(fā)者只需要定義想執(zhí)行的任務(wù)并追加到適當(dāng)?shù)腄ispatch Queue中,GCD就能生成必要的線程并計(jì)劃執(zhí)行任務(wù)。
  • GCD(Grand Central Dispatch)是基于C語(yǔ)言開(kāi)發(fā)的一套多線程開(kāi)發(fā)機(jī)制,是完全面向過(guò)程的。

GCD基本概念

這就需要上一篇博客里的基本知識(shí)了(不清楚去看下)iOS開(kāi)發(fā)之多線程編程總結(jié)(一)

任務(wù)和隊(duì)列

  • 任務(wù):就是執(zhí)行操作的意思,換句話說(shuō)就是你在線程中執(zhí)行的那段代碼。在GCD中是放在block中的。執(zhí)行任務(wù)有兩種方式:同步執(zhí)行異步執(zhí)行。兩者的主要區(qū)別是:是否具備開(kāi)啟新線程的能力。

    1. 同步執(zhí)行(sync):只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開(kāi)啟新線程的能力
    • 必須等待當(dāng)前語(yǔ)句執(zhí)行完畢,才會(huì)執(zhí)行下一條語(yǔ)句
    • 不會(huì)開(kāi)啟線程
    • 在當(dāng)前主線程執(zhí)行 block 的任務(wù)
    • dispatch_sync(queue, block);
    1. ** 異步執(zhí)行(async)**:可以在新的線程中執(zhí)行任務(wù),具備開(kāi)啟新線程的能力
    • 不用等待當(dāng)前語(yǔ)句執(zhí)行完畢,就可以執(zhí)行下一條語(yǔ)句
    • 會(huì)開(kāi)啟線程執(zhí)行 block 的任務(wù)
    • 異步是多線程的代名詞
    • dispatch_async(queue, block);
  • 隊(duì)列:這里的隊(duì)列指任務(wù)隊(duì)列,即用來(lái)存放任務(wù)的隊(duì)列。隊(duì)列是一種特殊的線性表,采用FIFO(先進(jìn)先出)的原則,即新任務(wù)總是被插入到隊(duì)列的末尾,而讀取任務(wù)的時(shí)候總是從隊(duì)列的頭部開(kāi)始讀取。每讀取一個(gè)任務(wù),則從隊(duì)列中釋放一個(gè)任務(wù)。在GCD中有四種隊(duì)列:串行隊(duì)列、并發(fā)隊(duì)列、主隊(duì)列全局隊(duì)列。

    1. 串行隊(duì)列(Serial Dispatch Queue):讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行(一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù))

      • 一次只能"調(diào)度"一個(gè)任務(wù)
      • dispatch_queue_create("queue", NULL);
        或者dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    2. 并發(fā)隊(duì)列(Concurrent Dispatch Queue):可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開(kāi)啟多個(gè)線程同時(shí)執(zhí)行任務(wù)),

      • 一次可以"調(diào)度"多個(gè)任務(wù)
      • 并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
      • dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    3. 主隊(duì)列

      • 專(zhuān)門(mén)用來(lái)在主線程上調(diào)度任務(wù)的隊(duì)列
      • 不會(huì)開(kāi)啟線程
      • 在主線程空閑時(shí)才會(huì)調(diào)度隊(duì)列中的任務(wù)在主線程執(zhí)行
      • dispatch_get_main_queue();
    4. 全局隊(duì)列

      • 執(zhí)行過(guò)程和并發(fā)隊(duì)列一致,參考并發(fā)隊(duì)列
      • dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

小結(jié): 在以后的使用中,記住下面的就可以了!

  1. 開(kāi)不開(kāi)線程由執(zhí)行任務(wù)的函數(shù)決定
  • 異步開(kāi),異步是多線程的代名詞
  • 同步不開(kāi)
  1. 開(kāi)幾條線程由隊(duì)列決定
  • 串行隊(duì)列開(kāi)一條線程(GCD會(huì)開(kāi)一條,NSOperation Queue最大并發(fā)數(shù)為1時(shí)也可能開(kāi)多條)
  • 并發(fā)隊(duì)列開(kāi)多條線程,具體能開(kāi)的線程數(shù)量由底層線程池決定

GCD的使用

今天我們學(xué)習(xí)下面圖片的相關(guān)知識(shí)點(diǎn),Demo下載鏈接會(huì)在文章最后給出來(lái)


GCD知識(shí)點(diǎn).png

簡(jiǎn)單來(lái)看一段代碼:異步下載圖片

#pragma mark - 1.異步下載圖片
- (IBAction)downLoadAction:(UIButton *)sender {
    self.imageView.image = nil;
    //獲取全局隊(duì)列
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //異步下載圖片
        NSURL *url=[NSURL URLWithString:@"https://p1.bpimg.com/524586/475bc82ff016054ds.jpg"];
        //將資源轉(zhuǎn)換為二進(jìn)制
        NSData *data=[NSData dataWithContentsOfURL:url];
        //將二進(jìn)制轉(zhuǎn)化為圖片
        UIImage *image=[UIImage imageWithData:data];
        
        //獲取主隊(duì)列,更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            //給圖片控件賦值
            self.imageView.image=image;
        });
    });
}

NSThread對(duì)比可以發(fā)現(xiàn)

  • 所有的代碼寫(xiě)在一起的,讓代碼更加簡(jiǎn)單,易于閱讀和維護(hù)
  • NSThread 通過(guò) @selector 指定要執(zhí)行的方法,代碼分散
  • GCD 通過(guò) block 指定要執(zhí)行的代碼,代碼集中
  • 使用 GCD 不需要管理線程的創(chuàng)建/銷(xiāo)毀/復(fù)用的過(guò)程!程序員不用關(guān)心線程的生命周期
  • 如果要開(kāi)多個(gè)線程 NSThread 必須實(shí)例化多個(gè)線程對(duì)象或者使用分類(lèi)方法
  • NSThread 靠 NSObject 的分類(lèi)方法實(shí)現(xiàn)的線程間通訊,GCD 靠 block
  • dispatch_async(queue, block);就是異步執(zhí)行一個(gè)隊(duì)列里面的任務(wù)block。每個(gè)block之間是異步執(zhí)行的,但是block里面的代碼是順序執(zhí)行的!
  • dispatch_sync(queue, block);就是同步執(zhí)行一個(gè)隊(duì)列里面的任務(wù)block

1. 串行隊(duì)列(Serial Dispatch Queue)

串行隊(duì)列的創(chuàng)建:
 dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
 dispatch_queue_t queue = dispatch_queue_create("queue", NULL);
串行隊(duì)列同步和異步執(zhí)行Demo:
 #pragma mark - 串行隊(duì)列同步和串行隊(duì)列異步
 //串行隊(duì)列同步
 - (void)serialQueueSyncMethod{
     //創(chuàng)建隊(duì)列
     dispatch_queue_t queue = dispatch_queue_create("serialQueueSyncMethod", DISPATCH_QUEUE_SERIAL);
     //執(zhí)行任務(wù)
     for (int i = 0; i < 6; i++) {
         NSLog(@"mainThread--->%d",i);
         dispatch_sync(queue, ^{
             NSLog(@"Current Thread=%@---->%d-----",[NSThread currentThread],i);
         });
     }
     NSLog(@"串行隊(duì)列同步end");
  }

 //串行隊(duì)列異步
  - (void)serialQueueAsyncMethod{
     dispatch_queue_t queue = dispatch_queue_create("serialQueueAsyncMethod", DISPATCH_QUEUE_SERIAL);
     for (int i = 0; i < 6; i++) {
         NSLog(@"mainThread--->%d",i);
         dispatch_async(queue, ^{
             NSLog(@"Current Thread=%@---->%d-----",[NSThread currentThread],i);
         });
     }
     NSLog(@"串行隊(duì)列異步end");
  }   
串行隊(duì)列 同步執(zhí)行結(jié)果:
2016-11-03 17:16:35.794 ThreadDemo[27088:5268309] mainThread--->0
2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] Current Thread=<NSThread: 0x60800007ae40>{number = 1, name = main}---->0-----
2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] mainThread--->1
2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] Current Thread=<NSThread: 0x60800007ae40>{number = 1, name = main}---->1-----
2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] mainThread--->2
2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] Current Thread=<NSThread: 0x60800007ae40>{number = 1, name = main}---->2-----
2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] mainThread--->3
2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] Current Thread=<NSThread: 0x60800007ae40>{number = 1, name = main}---->3-----
2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] mainThread--->4
2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] Current Thread=<NSThread: 0x60800007ae40>{number = 1, name = main}---->4-----
2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] mainThread--->5
2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] Current Thread=<NSThread: 0x60800007ae40>{number = 1, name = main}---->5-----
2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] 串行隊(duì)列同步end
串行隊(duì)列 異步執(zhí)行結(jié)果:
2016-11-03 17:22:25.074 ThreadDemo[27122:5273206] mainThread--->0
2016-11-03 17:22:25.074 ThreadDemo[27122:5273206] mainThread--->1
2016-11-03 17:22:25.074 ThreadDemo[27122:5273252] Current Thread=<NSThread: 0x60800007f380>{number = 5, name = (null)}---->0-----
2016-11-03 17:22:25.074 ThreadDemo[27122:5273206] mainThread--->2
2016-11-03 17:22:25.074 ThreadDemo[27122:5273252] Current Thread=<NSThread: 0x60800007f380>{number = 5, name = (null)}---->1-----
2016-11-03 17:22:25.074 ThreadDemo[27122:5273206] mainThread--->3
2016-11-03 17:22:25.075 ThreadDemo[27122:5273252] Current Thread=<NSThread: 0x60800007f380>{number = 5, name = (null)}---->2-----
2016-11-03 17:22:25.075 ThreadDemo[27122:5273206] mainThread--->4
2016-11-03 17:22:25.075 ThreadDemo[27122:5273252] Current Thread=<NSThread: 0x60800007f380>{number = 5, name = (null)}---->3-----
2016-11-03 17:22:25.075 ThreadDemo[27122:5273206] mainThread--->5
2016-11-03 17:22:25.075 ThreadDemo[27122:5273252] Current Thread=<NSThread: 0x60800007f380>{number = 5, name = (null)}---->4-----
2016-11-03 17:22:25.075 ThreadDemo[27122:5273206] 串行隊(duì)列異步end
2016-11-03 17:22:25.075 ThreadDemo[27122:5273252] Current Thread=<NSThread: 0x60800007f380>{number = 5, name = (null)}---->5-----
小結(jié):
  • 從串行隊(duì)列同步執(zhí)行結(jié)果看出打印是交替執(zhí)行的!從打印中看到number=1,說(shuō)明線程block任務(wù)是在主線程中執(zhí)行的。因?yàn)?strong>同步是不會(huì)開(kāi)辟線程的,所有當(dāng)前只有一個(gè)主線程MainThread。也就是說(shuō)串行隊(duì)列同步執(zhí)行不會(huì)開(kāi)辟線程,所有block任務(wù)之間是同步執(zhí)行的
  • 從串行隊(duì)列異步執(zhí)行結(jié)果看出打印并不是交替執(zhí)行的!從打印中看到number=5,說(shuō)明線程block的任務(wù)是在一個(gè)全新的線程中執(zhí)行的。因?yàn)?strong>異步是會(huì)開(kāi)辟線程的,所有當(dāng)前有主線程MainThread和子線程number=5。也就是說(shuō)串行隊(duì)列異步執(zhí)行會(huì)僅會(huì)開(kāi)辟一個(gè)新的線程,所有block任務(wù)之間是同步執(zhí)行的
  • 以先進(jìn)先出的方式,順序調(diào)度隊(duì)列中的任務(wù)執(zhí)行
  • 無(wú)論隊(duì)列中指定的任務(wù)函數(shù)是同步還是異步,都會(huì)等待前一個(gè)任務(wù)執(zhí)行完畢以后,再調(diào)度后面的任務(wù)

2. 并發(fā)隊(duì)列(Concurrent Dispatch Queue)

并發(fā)隊(duì)列創(chuàng)建
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
并發(fā)隊(duì)列同步和異步執(zhí)行Demo
 #pragma mark - 并行隊(duì)列同步和并行隊(duì)列異步
 //并行隊(duì)列同步
 - (void)concurrentQueueSyncMethod{
     dispatch_queue_t queue = dispatch_queue_create("concurrentQueueSyncMethod", DISPATCH_QUEUE_CONCURRENT);
    
     for (int i = 0; i < 6; i++) {
         dispatch_sync(queue, ^{
             NSLog(@"Current Thread=%@---->%d-----",[NSThread currentThread],i);
         });
     }
     NSLog(@"并行隊(duì)列同步end");
  }

 //并行隊(duì)列異步
- (void)concurrentQueueAsyncMethod{
     dispatch_queue_t queue = dispatch_queue_create("concurrentQueueAsyncMethod", DISPATCH_QUEUE_CONCURRENT);

     for (int i = 0; i < 6; i++) {
         dispatch_async(queue, ^{
             NSLog(@"Current Thread=%@---->%d-----",[NSThread currentThread],i);
         });
     }
    
     NSLog(@"并行隊(duì)列異步end");
  }      
并發(fā) 隊(duì)列同步執(zhí)行結(jié)果:
2016-11-03 17:49:33.850 ThreadDemo[27176:5290096] Current Thread=<NSThread: 0x60000007bf00>{number = 1, name = main}---->0-----
2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread=<NSThread: 0x60000007bf00>{number = 1, name = main}---->1-----
2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread=<NSThread: 0x60000007bf00>{number = 1, name = main}---->2-----
2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread=<NSThread: 0x60000007bf00>{number = 1, name = main}---->3-----
2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread=<NSThread: 0x60000007bf00>{number = 1, name = main}---->4-----
2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread=<NSThread: 0x60000007bf00>{number = 1, name = main}---->5-----
2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] 并行隊(duì)列同步end
并發(fā)隊(duì)列 異步執(zhí)行結(jié)果:
2016-11-03 18:33:32.794 ThreadDemo[27283:5311953] 并行隊(duì)列異步end
2016-11-03 18:33:32.794 ThreadDemo[27283:5312009] Current Thread=<NSThread: 0x60800026b8c0>{number = 3, name = (null)}---->0-----
2016-11-03 18:33:32.794 ThreadDemo[27283:5312006] Current Thread=<NSThread: 0x600000269180>{number = 4, name = (null)}---->1-----
2016-11-03 18:33:32.794 ThreadDemo[27283:5312003] Current Thread=<NSThread: 0x600000268f80>{number = 6, name = (null)}---->3-----
2016-11-03 18:33:32.794 ThreadDemo[27283:5312174] Current Thread=<NSThread: 0x60800026b740>{number = 8, name = (null)}---->5-----
2016-11-03 18:33:32.794 ThreadDemo[27283:5312004] Current Thread=<NSThread: 0x60800026b7c0>{number = 5, name = (null)}---->2-----
2016-11-03 18:33:32.794 ThreadDemo[27283:5312173] Current Thread=<NSThread: 0x60800026b800>{number = 7, name = (null)}---->4-----
小結(jié):
  • 并發(fā)隊(duì)列同步執(zhí)行和串行隊(duì)列同步執(zhí)行一樣,都不會(huì)開(kāi)辟新線程,block任務(wù)之間是同步執(zhí)行的!
  • 并發(fā)隊(duì)列異步執(zhí)行結(jié)果中看到開(kāi)辟了多個(gè)線程,并且執(zhí)行順序也不是順序執(zhí)行。因?yàn)?strong>異步開(kāi)多線程的代名詞,并發(fā)是開(kāi)多條線程的代名詞
  • 有多個(gè)線程,操作進(jìn)來(lái)之后它會(huì)將這些隊(duì)列安排在可用的處理器上,同時(shí)保證先進(jìn)來(lái)的任務(wù)優(yōu)先處理。
  • 以先進(jìn)先出的方式,并發(fā)調(diào)度隊(duì)列中的任務(wù)執(zhí)行
  • 如果當(dāng)前調(diào)度的任務(wù)是同步執(zhí)行的,會(huì)等待任務(wù)執(zhí)行完成后,再調(diào)度后續(xù)的任務(wù)
  • 如果當(dāng)前調(diào)度的任務(wù)是異步執(zhí)行的,同時(shí)底層線程池有可用的線程資源,會(huì)再新的線程調(diào)度后續(xù)任務(wù)的執(zhí)行

3. 全局隊(duì)列(Global Dispatch Queue)

全局隊(duì)列基本知識(shí)
  • dispatch_get_global_queue函數(shù)來(lái)獲取
  • 全局隊(duì)列是所有應(yīng)用程序都能夠使用的并發(fā)隊(duì)列(Concurrent Dispatch Queue),沒(méi)必要通過(guò)dispatch_queue_create函數(shù)逐個(gè)生成并發(fā)隊(duì)列,只需要獲取Global Dispatch Queue即可
  • 是系統(tǒng)為了方便程序員開(kāi)發(fā)提供的,其工作表現(xiàn)與并發(fā)隊(duì)列一致其工作表現(xiàn)與并發(fā)隊(duì)列一致、其工作表現(xiàn)與并發(fā)隊(duì)列一致
  • Global Dispatch Queue有4個(gè)優(yōu)先級(jí),分別是高優(yōu)先級(jí)、默認(rèn)優(yōu)先級(jí)、低優(yōu)先級(jí)、后臺(tái)優(yōu)先級(jí)!因?yàn)閄NU內(nèi)核用于Global Dispatch Queue的線程并不能保證實(shí)時(shí)性,因此執(zhí)行優(yōu)先級(jí)知識(shí)大概的判斷和區(qū)分。
  #define DISPATCH_QUEUE_PRIORITY_HIGH 2
  #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
  #define DISPATCH_QUEUE_PRIORITY_LOW (-2)
  #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
全局隊(duì)列同步和異步執(zhí)行Demo
 #pragma mark -全局隊(duì)列同步和全局隊(duì)列異步(工作表現(xiàn)與并發(fā)隊(duì)列一致)
 //全局隊(duì)列同步
 - (void)globalSyncMethod{
     //獲取全局隊(duì)列
       dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     //執(zhí)行任務(wù)
     for (int i = 0; i < 10; ++i) {
         dispatch_sync(queue, ^{
             NSLog(@"global_queue_sync%@---->%d----",[NSThread currentThread],i);
         });
     }
     NSLog(@"global_queue_sync_end");
 }

 //全局隊(duì)列異步
 - (void)globalAsyncMethod{
     //獲取全局隊(duì)列
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     //執(zhí)行任務(wù)
     for (int i = 0; i < 10; ++i) {
         dispatch_async(queue, ^{
             NSLog(@"global_queue_async%@---->%d----",[NSThread currentThread],i);
         });
     }
     NSLog(@"global_queue_async_end");
 }
全局隊(duì)列 同步執(zhí)行結(jié)果:
2016-11-03 19:06:02.650 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->0----
2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->1----
2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->2----
2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->3----
2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->4----
2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->5----
2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->6----
2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->7----
2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->8----
2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->9----
2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync_end
全局隊(duì)列 異步執(zhí)行結(jié)果:
2016-11-03 19:12:00.242 ThreadDemo[27430:5333537] global_queue_async_end
2016-11-03 19:12:00.242 ThreadDemo[27430:5334057] global_queue_async<NSThread: 0x608000266100>{number = 5, name = (null)}---->0----
2016-11-03 19:12:00.242 ThreadDemo[27430:5334053] global_queue_async<NSThread: 0x600000263c80>{number = 6, name = (null)}---->1----
2016-11-03 19:12:00.242 ThreadDemo[27430:5334056] global_queue_async<NSThread: 0x600000263cc0>{number = 7, name = (null)}---->2----
2016-11-03 19:12:00.242 ThreadDemo[27430:5334063] global_queue_async<NSThread: 0x608000268500>{number = 8, name = (null)}---->3----
2016-11-03 19:12:00.242 ThreadDemo[27430:5334064] global_queue_async<NSThread: 0x600000263b80>{number = 9, name = (null)}---->4----
2016-11-03 19:12:00.243 ThreadDemo[27430:5334057] global_queue_async<NSThread: 0x608000266100>{number = 5, name = (null)}---->5----
2016-11-03 19:12:00.243 ThreadDemo[27430:5334053] global_queue_async<NSThread: 0x600000263c80>{number = 6, name = (null)}---->7----
2016-11-03 19:12:00.243 ThreadDemo[27430:5334065] global_queue_async<NSThread: 0x600000073600>{number = 10, name = (null)}---->6----
2016-11-03 19:12:00.243 ThreadDemo[27430:5334056] global_queue_async<NSThread: 0x600000263cc0>{number = 7, name = (null)}---->8----
2016-11-03 19:12:00.243 ThreadDemo[27430:5334066] global_queue_async<NSThread: 0x608000267d40>{number = 11, name = (null)}---->9----

4. 主隊(duì)列(Main Dispatch Queue)

主隊(duì)列基本知識(shí)
  • dispatch_get_main_queue()函數(shù)來(lái)獲取
  • 專(zhuān)門(mén)用來(lái)在主線程上調(diào)度任務(wù)的隊(duì)列
  • 不會(huì)開(kāi)啟線程
  • 以先進(jìn)先出的方式,在主線程空閑時(shí)才會(huì)調(diào)度隊(duì)列中的任務(wù)在主線程執(zhí)行
  • 如果當(dāng)前主線程正在有任務(wù)執(zhí)行,那么無(wú)論主隊(duì)列中當(dāng)前被添加了什么任務(wù),都不會(huì)被調(diào)度
主隊(duì)列同步和異步執(zhí)行Demo
 #pragma mark -主隊(duì)列同步和主隊(duì)列異步
 //主隊(duì)列同步
 - (void)mainSyncMethod{
     //獲取主隊(duì)列
     dispatch_queue_t queue = dispatch_get_main_queue();
     //執(zhí)行任務(wù)
     for (int i = 0; i < 10; ++i) {
         dispatch_sync(queue, ^{
             NSLog(@"main_queue_sync%@---->%d----",[NSThread currentThread],i);
         });
     }
     NSLog(@"main_queue_sync_end");
 }

 //主隊(duì)列異步
 - (void)mainAsyncMethod{
     //獲取主隊(duì)列
     dispatch_queue_t queue = dispatch_get_main_queue();
     //執(zhí)行任務(wù)
     for (int i = 0; i < 10; ++i) {
         dispatch_async(queue, ^{
            NSLog(@"main_queue_async%@---->%d----",[NSThread currentThread],i);
         });
     }
     NSLog(@"main_queue_async_end");
 }
主隊(duì)列 同步執(zhí)行結(jié)果:
主線程和主隊(duì)列相互等待造成死鎖,程序會(huì)直接卡死!
原因:源代碼在Main Dispatch Queue 即主隊(duì)列中執(zhí)行指定的block任務(wù),并等待其結(jié)束。而其實(shí)在主線程中正在執(zhí)行這些源代碼,所以無(wú)法執(zhí)行追加到Main Dispatch Queue 的block任務(wù)。
下面例子也一樣:
dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        dispatch_sync(queue, ^{
            NSLog(@"main_queue_sync%@----",[NSThread currentThread]);
        });
    });

主隊(duì)列 異步執(zhí)行結(jié)果:
2016-11-03 19:45:38.154 ThreadDemo[27501:5349956] main_queue_async_end
2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->0----
2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->1----
2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->2----
2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->3----
2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->4----
2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->5----
2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->6----
2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->7----
2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->8----
2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->9----

5.延遲執(zhí)行(dispatch_after)

  • 在GCD中我們使用dispatch_after()函數(shù)來(lái)延遲執(zhí)行隊(duì)列中的任務(wù), dispatch_after()是異步執(zhí)行隊(duì)列中的任務(wù)的,也就是說(shuō)使用dispatch_after()來(lái)執(zhí)行隊(duì)列中的任務(wù)不會(huì)阻塞當(dāng)前任務(wù)。等到延遲時(shí)間到了以后就會(huì)開(kāi)辟一個(gè)新的線程然后執(zhí)行隊(duì)列中的任務(wù)。
  • dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);來(lái)創(chuàng)建
  • 經(jīng)過(guò)我的猜測(cè)測(cè)試(看不到源碼),你們也思考一下這個(gè)延遲函數(shù)的實(shí)現(xiàn)過(guò)程。底層實(shí)現(xiàn)應(yīng)該是dispath_async函數(shù)追加block到Main Dispatch Queue等相應(yīng)隊(duì)列,偽代碼如下(以Main Dispatch Queue為例):
 dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
 delay(2){
 dispatch_async(queue, ^{
     dispatch_async(dispatch_get_main_queue(), ^{
     add task to mainQueue
    });
  });
 }
延遲執(zhí)行(dispatch_after)Demo
#pragma mark -  延遲執(zhí)行
 - (void)GCDAfterRunMethod{
     // 循環(huán)5次
     for (int i =0; i < 10000; i++ ) {
         NSLog(@"let's go %d",i);
         // 設(shè)置2秒后執(zhí)行block
         dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC));
         dispatch_after(time, dispatch_get_main_queue(), ^{
             NSLog(@"This is my %d number!%@",i,[NSThread currentThread]);
         });
     }
       //等價(jià)于下面實(shí)現(xiàn)
 //    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
 //    for (int i = 0; i < 10000; i++) {  //        NSLog(@"let's go %d",i);
 //        dispatch_async(queue, ^{
 //           dispatch_async(dispatch_get_main_queue(), ^{  //                NSLog(@"This is my %d number!%@",i,[NSThread currentThread]);
 //           });
 //        });
 //    }
 }
注意:
  • dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);
  • 第二個(gè)參數(shù):指定要追加處理的Dispatch Queue
  • 第三個(gè)參數(shù):指定要執(zhí)行處理的Block
  • 第一個(gè)參數(shù):指定時(shí)間用的dispatch_time_t類(lèi)型的值,該值是使用dispatch_time函數(shù)或者dispatch_walltime函數(shù)生成
    1.dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC));
    里面有兩個(gè)參數(shù):
  參數(shù)1獲取指定的時(shí)間開(kāi)始,通常為DISPATCH_TIME_NOW,定義:
  #define DISPATCH_TIME_NOW (0ull)//現(xiàn)在
  #define DISPATCH_TIME_FOREVER (~0ull)//永遠(yuǎn),意味著你用它你的任務(wù)永遠(yuǎn)不會(huì)執(zhí)行了!
參數(shù)2是從參數(shù)1延遲指定的時(shí)間來(lái)執(zhí)行任務(wù),也是一個(gè)時(shí)間
#define NSEC_PER_SEC 1000000000ull
#define NSEC_PER_MSEC 1000000ull
#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull
ull是C語(yǔ)言的數(shù)值字面量,是顯示表明類(lèi)型是使用的字符串(表示“unsigned long long”)
NSEC_PER_SEC 是秒的單位數(shù)量級(jí) 等價(jià)于1億納秒
上面代碼中提到的(int64_t)(2 * NSEC_PER_SEC))就是2秒了

2.使用dispatch_walltime函數(shù)生成dispatch_time_t,用于計(jì)算絕對(duì)時(shí)間,例如在dispatch_after函數(shù)中指定2016年11月3日21時(shí)45分1秒這一絕對(duì)時(shí)間,可粗略的鬧鐘功能來(lái)使用。也就是到指定時(shí)間就會(huì)執(zhí)行任務(wù)

//由NSDate類(lèi)對(duì)象獲取dispatch_time_t傳遞給dispatch_after函數(shù)使用
dispatch_time_t  getDispatchTimeByDate(NSDate *date){
NSTimeInterval interval;
double second,subSecond;
struct timespec time;
dispatch_time_t milestone;
   
interval = [date timeIntervalSince1970];
   
subSecond = modf(interval, &second);
time.tv_sec = second;
time.tv_nsec = subSecond * NSEC_PER_SEC;

milestone = dispatch_walltime(&time, 0);
return milestone;
}

6. 更改優(yōu)先級(jí)(dispatch_set_target_queue)

  • dispatch_queue_create函數(shù)生成的Dispatch Queue不管是 串行隊(duì)列(Serial Dispatch Queue) 還是 并發(fā)隊(duì)列(Concurrent Dispatch Queue) ,都是用與 默認(rèn)優(yōu)先級(jí)全局隊(duì)列(Global Dispatch Queue)相同執(zhí)行優(yōu)先級(jí)的線程。而全局隊(duì)列工作方式與并發(fā)隊(duì)列工作方式完全一致!

  • dispatch_set_target_queue(dispatch_object_t object,dispatch_queue_t _Nullable queue);函數(shù)來(lái)更改隊(duì)列優(yōu)先級(jí)

    • 第一個(gè)參數(shù):指定要變更優(yōu)先級(jí)的隊(duì)列(要更改隊(duì)列)
    • 第二個(gè)參數(shù):指定第一個(gè)參數(shù)(隊(duì)列)要和我們預(yù)期隊(duì)列執(zhí)行相同優(yōu)先級(jí)的隊(duì)列(目標(biāo)隊(duì)列)

1. dispatch_set_target_queue第一個(gè)Demo:

 - (void)GCDSetTargetQueueMethod{
    
     dispatch_queue_t targetQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    
     dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
     dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
      //更改優(yōu)先級(jí)
     dispatch_set_target_queue(queue1, targetQueue);
    
     for (int i = 0; i < 6; i++) {
         dispatch_async(queue1, ^{
         NSLog(@"queue1-currentThread = %@-->%d",[NSThread currentThread],i);
         });
     }
     for (int i = 0; i < 6; i++) {
          dispatch_async(queue2, ^{
          NSLog(@"queue2-----currentThread = %@----->%d",[NSThread currentThread],i);
          });
     }
 } 
打印結(jié)果:
2016-11-04 11:14:57.034 ThreadDemo[28477:5530919] queue2-----currentThread = <NSThread: 0x60000007e0c0>{number = 11, name = (null)}----->1
2016-11-04 11:14:57.034 ThreadDemo[28477:5530920] queue2-----currentThread = <NSThread: 0x60000007a140>{number = 12, name = (null)}----->2
2016-11-04 11:14:57.034 ThreadDemo[28477:5530918] queue2-----currentThread = <NSThread: 0x60000007a100>{number = 10, name = (null)}----->0
2016-11-04 11:14:57.034 ThreadDemo[28477:5530919] queue2-----currentThread = <NSThread: 0x60000007e0c0>{number = 11, name = (null)}----->3
2016-11-04 11:14:57.034 ThreadDemo[28477:5530912] queue1-currentThread = <NSThread: 0x60000007ec00>{number = 9, name = (null)}-->0
2016-11-04 11:14:57.034 ThreadDemo[28477:5530920] queue2-----currentThread = <NSThread: 0x60000007a140>{number = 12, name = (null)}----->5
2016-11-04 11:14:57.034 ThreadDemo[28477:5530921] queue2-----currentThread = <NSThread: 0x60000007c340>{number = 13, name = (null)}----->4
2016-11-04 11:14:57.035 ThreadDemo[28477:5530912] queue1-currentThread = <NSThread: 0x60000007ec00>{number = 9, name = (null)}-->1
2016-11-04 11:14:57.036 ThreadDemo[28477:5530912] queue1-currentThread = <NSThread: 0x60000007ec00>{number = 9, name = (null)}-->2
2016-11-04 11:14:57.036 ThreadDemo[28477:5530912] queue1-currentThread = <NSThread: 0x60000007ec00>{number = 9, name = (null)}-->3
2016-11-04 11:14:57.036 ThreadDemo[28477:5530912] queue1-currentThread = <NSThread: 0x60000007ec00>{number = 9, name = (null)}-->4
2016-11-04 11:14:57.037 ThreadDemo[28477:5530912] queue1-currentThread = <NSThread: 0x60000007ec00>{number = 9, name = (null)}-->5
打印解釋?zhuān)?/strong>
  • queue1和queue2設(shè)置的目標(biāo)隊(duì)列是全局隊(duì)列(并發(fā)),也就是允許要更改的隊(duì)列可以開(kāi)辟多條線程(可以超過(guò)一條)
  • 代碼中queue1變更和低優(yōu)先級(jí)的全局隊(duì)列一樣優(yōu)先級(jí)的串行隊(duì)列,queue2是dispatch_queue_create創(chuàng)建和默認(rèn)優(yōu)先級(jí)的全局隊(duì)列一樣優(yōu)先級(jí)的并發(fā)隊(duì)列。
  • queue1和queue2都是異步執(zhí)行,都會(huì)開(kāi)辟線程,queue2是并發(fā)隊(duì)列所以打印中看到有多個(gè)線程,并且任務(wù)之間也不是順序執(zhí)行。queue1是串行隊(duì)列,所以只會(huì)開(kāi)辟一個(gè)線程,任務(wù)會(huì)順序執(zhí)行。
  • queue2隊(duì)列的優(yōu)先級(jí)比queue1隊(duì)列的優(yōu)先級(jí)要高,從打印中可以看到queue2的確比queue1任務(wù)要普遍先執(zhí)行。為什么要說(shuō)普遍呢,因?yàn)閺慕Y(jié)果上看到queue1有一個(gè)任務(wù)是在queue2里任務(wù)沒(méi)有執(zhí)行完畢也執(zhí)行了,為什么會(huì)出現(xiàn)這個(gè)問(wèn)題呢??
在上面全局隊(duì)列中說(shuō)到了優(yōu)先級(jí)的事情
因?yàn)閄NU內(nèi)核用于Global Dispatch Queue的線程并不能保證實(shí)時(shí)性,因此執(zhí)行優(yōu)先級(jí)知識(shí)大概的判斷和區(qū)分,
所有我們不能完全依賴這個(gè)優(yōu)先級(jí)來(lái)做隊(duì)列的順序事情,否則會(huì)出現(xiàn)問(wèn)題!切記

2. dispatch_set_target_queue第一個(gè)Demo:

  • dispatch_set_target_queue除了能用來(lái)設(shè)置隊(duì)列的優(yōu)先級(jí)之外,還能夠創(chuàng)建隊(duì)列的執(zhí)行層次
    ,當(dāng)我們想讓不同隊(duì)列中的任務(wù)同步的執(zhí)行時(shí),我們可以創(chuàng)建一個(gè)串行隊(duì)列,然后將這些隊(duì)列的target指向新創(chuàng)建的隊(duì)列即可,比如:


    隊(duì)列的執(zhí)行層次.png
 - (void)GCDSetTargetQueueMethod{
     dispatch_queue_t targetQueue = dispatch_queue_create("targetQueue", DISPATCH_QUEUE_SERIAL);//目標(biāo)隊(duì)列
     dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);//串行隊(duì)列
     dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);//并發(fā)隊(duì)列
     //設(shè)置參考
     dispatch_set_target_queue(queue1, targetQueue);
     dispatch_set_target_queue(queue2, targetQueue);
    
     for (int i = 0; i < 6; i++) {
       dispatch_async(queue1, ^{
           NSLog(@"queue1-currentThread = %@-->%d",[NSThread currentThread],i);
       });
   }
   for (int i = 0; i < 6; i++) {
         dispatch_async(queue2, ^{
             NSLog(@"queue2-currentThread = %@-->%d",[NSThread currentThread],i);
         });
     }
    
 }
打印結(jié)果:
2016-11-04 11:34:34.722 ThreadDemo[28551:5540844] queue1-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->0
2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->1
2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->2
2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->3
2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->4
2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->5
2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->0
2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->1
2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->2
2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->3
2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->4
2016-11-04 11:34:34.725 ThreadDemo[28551:5540844] queue2-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->5
打印解釋?zhuān)?/strong>
  • queue1和queue2設(shè)置的目標(biāo)隊(duì)列是串行隊(duì)列,也就是允許要更改的隊(duì)列可以開(kāi)辟一條線程。
  • queue1和queue2優(yōu)先級(jí)一樣,他兩執(zhí)行順序相當(dāng)于一個(gè)串行隊(duì)列異步執(zhí)行!
  • queue1和queue2隊(duì)列以targetQueue隊(duì)列為參照對(duì)象,那么queue1和queue2中的任務(wù)將按照targetQueue的隊(duì)列處理。
  • 適用場(chǎng)景:一般都是把一個(gè)任務(wù)放到一個(gè)串行的queue中,如果這個(gè)任務(wù)被拆分了,被放置到多個(gè)串行的queue中,但實(shí)際還是需要這個(gè)任務(wù)同步執(zhí)行,那么就會(huì)有問(wèn)題,因?yàn)槎鄠€(gè)串行queue之間是并行的。這時(shí)候dispatch_set_target_queue將起到作用。

** dispatch_set_target_queue小結(jié):**

  • dispatch_set_target_queue可以更改Dispatch Queue優(yōu)先級(jí)。
  • dispatch_set_target_queue可以更改隊(duì)列的執(zhí)行層次,隊(duì)列里的任務(wù)將會(huì)按照目標(biāo)隊(duì)列(target Queue)的隊(duì)列來(lái)處理

7. 任務(wù)組Dispatch Group

  • GCD的任務(wù)組在開(kāi)發(fā)中是經(jīng)常被使用到,當(dāng)你一組任務(wù)結(jié)束后再執(zhí)行一些操作時(shí),使用任務(wù)組在合適不過(guò)了。dispatch_group的職責(zé)就是當(dāng)隊(duì)列中的所有任務(wù)都執(zhí)行完畢后在去做一些操作,也就是說(shuō)在任務(wù)組中執(zhí)行的隊(duì)列,當(dāng)隊(duì)列中的所有任務(wù)都執(zhí)行完畢后就會(huì)發(fā)出一個(gè)通知來(lái)告訴用戶任務(wù)組中所執(zhí)行的隊(duì)列中的任務(wù)執(zhí)行完畢了。關(guān)于將隊(duì)列放到任務(wù)組中執(zhí)行有兩種方式,一種是使用dispatch_group_async()函數(shù),將隊(duì)列與任務(wù)組進(jìn)行關(guān)聯(lián)并自動(dòng)執(zhí)行隊(duì)列中的任務(wù)。另一種方式是手動(dòng)的將隊(duì)列與組進(jìn)行關(guān)聯(lián)然后使用異步將隊(duì)列進(jìn)行執(zhí)行,也就是dispatch_group_enter()dispatch_group_leave()方法的使用。下方就給出詳細(xì)的介紹。
1.隊(duì)列與組自動(dòng)關(guān)聯(lián)并執(zhí)行
  • 首先我們來(lái)介紹dispatch_group_async()函數(shù)的使用方式,該函數(shù)會(huì)將隊(duì)列與相應(yīng)的任務(wù)組進(jìn)行關(guān)聯(lián),并且自動(dòng)執(zhí)行。當(dāng)與任務(wù)組關(guān)聯(lián)的隊(duì)列中的任務(wù)都執(zhí)行完畢后,會(huì)通過(guò)dispatch_group_notify()函數(shù)發(fā)出通知告訴用戶任務(wù)組中的所有任務(wù)都執(zhí)行完畢了。使用通知的方式是不會(huì)阻塞當(dāng)前線程的,如果你使用dispatch_group_wait()函數(shù),那么就會(huì)阻塞當(dāng)前線程,直到任務(wù)組中的所有任務(wù)都執(zhí)行完畢。
 //自動(dòng)執(zhí)行任務(wù)組
 - (void)GCDAutoDispatchGroupMethod{
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     dispatch_group_t group = dispatch_group_create();
   
     for (int i = 0; i < 6; i++) {
           dispatch_group_async(group, queue, ^{
             NSLog(@"current Thread = %@----->%d",[NSThread currentThread],i);
         });
     }

     dispatch_group_notify(group, dispatch_get_main_queue(), ^{
         NSLog(@"current Thread = %@----->這是最后執(zhí)行",[NSThread   currentThread]);
     });
}
打印結(jié)果:
2016-11-04 15:11:34.010 ThreadDemo[29225:5641180] current Thread = <NSThread: 0x600000468200>{number = 5, name = (null)}----->2
2016-11-04 15:11:34.010 ThreadDemo[29225:5641193] current Thread = <NSThread: 0x60800007f900>{number = 3, name = (null)}----->0
2016-11-04 15:11:34.011 ThreadDemo[29225:5641438] current Thread = <NSThread: 0x608000467980>{number = 8, name = (null)}----->5
2016-11-04 15:11:34.010 ThreadDemo[29225:5641178] current Thread = <NSThread: 0x608000467640>{number = 6, name = (null)}----->3
2016-11-04 15:11:34.010 ThreadDemo[29225:5641177] current Thread = <NSThread: 0x6080004675c0>{number = 4, name = (null)}----->1
2016-11-04 15:11:34.010 ThreadDemo[29225:5641437] current Thread = <NSThread: 0x600000467f40>{number = 7, name = (null)}----->4
2016-11-04 15:11:34.011 ThreadDemo[29225:5641137] current Thread = <NSThread: 0x60800007e200>{number = 1, name = main}----->這是最后執(zhí)行
  • 上面的函數(shù)就是使用dispatch_group_async()函數(shù)將隊(duì)列與任務(wù)組進(jìn)行關(guān)聯(lián)并執(zhí)行。首先我們創(chuàng)建了一個(gè)全局隊(duì)列(并發(fā)),然后又創(chuàng)建了一個(gè)類(lèi)型為dispatch_group_t的任務(wù)組group。使用dispatch_group_async()函數(shù)將兩者進(jìn)行關(guān)聯(lián)并執(zhí)行。使用dispatch_group_notify()函數(shù)進(jìn)行監(jiān)聽(tīng)group中隊(duì)列的執(zhí)行結(jié)果,如果執(zhí)行完畢后,我們就在主線程中對(duì)結(jié)果進(jìn)行處理。

  • dispatch_group_notify()函數(shù)有兩個(gè)參數(shù)一個(gè)是發(fā)送通知的group,另一個(gè)是處理返回結(jié)果的隊(duì)列。

  • dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);函數(shù)與dispatch_async函數(shù)相同,都追加block到指定的Dispatch Queue中,與dispatch_async不同的是指定生成的Dispatch Group為第一個(gè)參數(shù)。指定的block屬于指定的Dispatch Group,不是Dispatch Queue,切記!

  • 無(wú)論向什么樣的Dispatch Queue中追加處理,使用Dispatch Group都可監(jiān)視這些處理執(zhí)行的結(jié)束。一旦檢測(cè)到所有處理執(zhí)行結(jié)束,就可將結(jié)束的處理追加到Dispatch Queue中。這就是使用Dispatch Group的原因。

  • 在追加到Dispatch Group中的處理全部執(zhí)行結(jié)束時(shí),代碼中使用的dispatch_group_notify函數(shù)會(huì)將執(zhí)行的Block追加到Dispatch Queue中,將第一個(gè)參數(shù)指定為要監(jiān)視的Dispatch Group。在追加到該Dispatch Group的全部處理執(zhí)行結(jié)束時(shí),將第三個(gè)參數(shù)的Block追加到第二個(gè)參數(shù)的Dispatch Queue中。在dispatch_group_notify函數(shù)中不管指定什么樣的Dispatch Queue,屬于Dispatch Group的全部處理在追加指定的Block時(shí)都已執(zhí)行結(jié)束。

  • 另外,在Dispatch Group中也可以使用dispatch_group_wait函數(shù)僅等待全部處理執(zhí)行結(jié)束。

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, ^{NSLog(@"blk0");});
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

  • dispatch_group_wait函數(shù)的第二個(gè)參數(shù)指定為等待時(shí)間(超時(shí))。它屬于dispatch_time_t類(lèi)型的值。代碼中使用的DISPATCH_TIME_FOREVER,意味著永久等待。只要屬于Dispatch Group的操作尚未執(zhí)行結(jié)束,就會(huì)一直等待,中途不能取消。

    指定等待時(shí)間為1微秒時(shí),應(yīng)做如下處理:

   //等待group處理結(jié)束
//    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
   dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1*USEC_PER_SEC));//1微秒
   long result = dispatch_group_wait(group, time);
   if (result == 0) {
       //屬于Dispatch Group 的block任務(wù)全部處理結(jié)束
       NSLog(@"Dispatch Group全部處理完畢");

   }else{
       //屬于Dispatch Group 的block任務(wù)還在處理中
       NSLog(@"Dispatch Group正在處理");
   }

  • 如果dispatch_group_wait函數(shù)的返回值不為0,就意味著雖然經(jīng)過(guò)了指定的時(shí)間,但屬于Dispatch Group的某一個(gè)處理還在執(zhí)行中。如果返回值為0,那么全部處理執(zhí)行結(jié)束。當(dāng)?shù)却龝r(shí)間為DISPATCH_TIME_FOREVER,由dispatch_group_wait函數(shù)返回時(shí),屬于Dispatch Group的處理必定全部執(zhí)行結(jié)束,因此返回值恒為0。

  • 這里的“等待”是什么意思?這意味著一旦調(diào)用dispatch_group_wait函數(shù),該函數(shù)就處于調(diào)用的狀態(tài)而不返回。即執(zhí)行dispatch_group_wait函數(shù)的現(xiàn)在的線程(當(dāng)前線程)停止。在經(jīng)過(guò)dispatch_group_wait函數(shù)中指定的時(shí)間或?qū)儆谥付―ispatch Group的處理全部執(zhí)行結(jié)束之前,執(zhí)行該函數(shù)的線程停止,屬于阻塞狀態(tài)。

  • 指定DISPACH_TIME_NOW,則不用任何等待即可判定屬于Dispatch Group的處理是否執(zhí)行結(jié)束。

long result = diaptach_group_wait(group, DISPACH_TIME_NOW);
  • 在主線程的RunLoop的每次循環(huán)中,可檢查執(zhí)行是否結(jié)束,從而不消耗多余的等待時(shí)間,雖然這樣有額可以,但一般在這種情況下,還是推薦用dispatch_group_notify函數(shù)追加結(jié)束處理到Main Dispatch Queue中。這是因?yàn)?code>dispatch_group_notify函數(shù)可以簡(jiǎn)化代碼。并且如果你用了diaptach_group_wait等待時(shí)間過(guò)長(zhǎng),中間不能取消隊(duì)列任務(wù)這就很坑了!
2. 隊(duì)列與組手動(dòng)關(guān)聯(lián)并執(zhí)行
  • 接下來(lái)我們將手動(dòng)的管理任務(wù)組與隊(duì)列中的關(guān)系,也就是不使用dispatch_group_async()函數(shù)。我們使用dispatch_group_enter()dispatch_group_leave()函數(shù)將隊(duì)列中的每次任務(wù)加入到到任務(wù)組中。首先我們使用dispatch_group_enter()函數(shù)進(jìn)入到任務(wù)組中,然后異步執(zhí)行隊(duì)列中的任務(wù),最后使用dispatch_group_leave()函數(shù)離開(kāi)任務(wù)組即可。下面的函數(shù)中我們使用了dispatch_group_wait()函數(shù),該函數(shù)的職責(zé)就是阻塞當(dāng)前線程,來(lái)等待任務(wù)組中的任務(wù)執(zhí)行完畢。該函數(shù)的第一個(gè)參數(shù)是所要等待的group,第二個(gè)參數(shù)是等待超時(shí)時(shí)間,此處我們?cè)O(shè)置的是DISPATCH_TIME_FOREVER,就說(shuō)明等待任務(wù)組的執(zhí)行永不超時(shí),直到任務(wù)組中所有任務(wù)執(zhí)行完畢。
//手動(dòng)執(zhí)行任務(wù)組
 - (void)GCDManualDispatchGroupMethod{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    for (int i = 0; i < 6; i++) {
        
        dispatch_group_enter(group);//進(jìn)入隊(duì)列組
        
        dispatch_async(queue, ^{
            NSLog(@"current Thread = %@----->%d",[NSThread currentThread],i);
            
            dispatch_group_leave(group);//離開(kāi)隊(duì)列組
        });
    }
    
    long result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);//阻塞當(dāng)前線程,直到所有任務(wù)執(zhí)行完畢才會(huì)繼續(xù)往下執(zhí)行
    if (result == 0) {
        //屬于Dispatch Group 的block任務(wù)全部處理結(jié)束
        NSLog(@"Dispatch Group全部處理完畢");

    }else{
        //屬于Dispatch Group 的block任務(wù)還在處理中
        NSLog(@"Dispatch Group正在處理");
    }
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"current Thread = %@----->這是最后執(zhí)行",[NSThread currentThread]);
    });
 }
打印結(jié)果:dispatch_group_wait()函數(shù)下方的print()函數(shù)在所有任務(wù)執(zhí)行完畢之前是不會(huì)被調(diào)用的,因?yàn)?code>dispatch_group_wait()會(huì)將當(dāng)前線程進(jìn)行阻塞。當(dāng)然雖然是手動(dòng)的將隊(duì)列與任務(wù)組進(jìn)行關(guān)聯(lián)的,感覺(jué)display_group_notify()函數(shù)還是好用的。
2016-11-04 16:19:13.802 ThreadDemo[29402:5678525] current Thread = <NSThread: 0x600000271780>{number = 5, name = (null)}----->2
2016-11-04 16:19:13.802 ThreadDemo[29402:5678527] current Thread = <NSThread: 0x608000077300>{number = 3, name = (null)}----->0
2016-11-04 16:19:13.802 ThreadDemo[29402:5678524] current Thread = <NSThread: 0x60800007d840>{number = 4, name = (null)}----->1
2016-11-04 16:19:13.803 ThreadDemo[29402:5678760] current Thread = <NSThread: 0x600000271cc0>{number = 8, name = (null)}----->5
2016-11-04 16:19:13.802 ThreadDemo[29402:5678545] current Thread = <NSThread: 0x60800007d000>{number = 6, name = (null)}----->3
2016-11-04 16:19:13.802 ThreadDemo[29402:5678544] current Thread = <NSThread: 0x60800007d040>{number = 7, name = (null)}----->4
2016-11-04 16:19:13.803 ThreadDemo[29402:5678489] Dispatch Group全部處理完畢
2016-11-04 16:19:13.804 ThreadDemo[29402:5678489] current Thread = <NSThread: 0x600000069dc0>{number = 1, name = main}----->這是最后執(zhí)行

8. 柵欄任務(wù)Dispatch_barrier_async

  • barrier顧名思義柵欄、障礙物的意思!
    在訪問(wèn)數(shù)據(jù)庫(kù)或文件時(shí),使用Serial Dispatch Queue可避免數(shù)據(jù)競(jìng)爭(zhēng)的問(wèn)題。

    寫(xiě)入處理確實(shí)不可與其他的寫(xiě)入處理以及包含讀取處理的其他某些處理并行執(zhí)行。但是如果讀取處理只是與讀取處理并行執(zhí)行,那么多個(gè)并行執(zhí)行就不會(huì)發(fā)生問(wèn)題。

    也就是說(shuō),為了高效率地進(jìn)行訪問(wèn),讀取處理追加到Concurrent Dispatch Queue中,寫(xiě)入出路在任何一個(gè)讀取處理沒(méi)有執(zhí)行的狀態(tài)下,追加到Serial Dispatch Queue中即可(在寫(xiě)入處理結(jié)束之前,讀取處理不可執(zhí)行)。

    雖然利用Dispatch Group和dispatch_set_target_queue函數(shù)也可實(shí)現(xiàn),但代碼會(huì)很復(fù)雜。有興趣的可以自己嘗試寫(xiě)寫(xiě)!
    CD為我們提供了更為聰明的解決辦法——dispatch_barrier_async函數(shù)。該函數(shù)同dispatch_queue_create函數(shù)生成的Concurrent Dispatch Queue一起使用。

 #pragma mark - Dispatch_barrier_async
 - (void)GCDBarrierAsyncMethod{
    
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    void(^blk1_reading)(void) = ^{
         NSLog(@"blk1---reading");
    };
    
    void(^blk2_reading)(void) = ^{
        NSLog(@"blk2---reading");
    };
    void(^blk3_reading)(void) = ^{
        NSLog(@"blk3---reading");
    };
    void(^blk4_reading)(void) = ^{
        NSLog(@"blk4---reading");
    };
    void(^blk_writing)(void) = ^{
        NSLog(@"blk---writing");
    };
        
    dispatch_async(concurrentQueue, blk1_reading);
    dispatch_async(concurrentQueue, blk2_reading);
    
    //添加追加操作,,會(huì)等待b1和b2全部執(zhí)行結(jié)束,執(zhí)行完成追加操作b,才會(huì)繼續(xù)并發(fā)執(zhí)行下面操作
    dispatch_barrier_async(concurrentQueue, blk_writing);
    
    dispatch_async(concurrentQueue, blk3_reading);
    dispatch_async(concurrentQueue, blk4_reading);

}
打印結(jié)果:
2016-11-04 17:02:13.202 ThreadDemo[29492:5700974] blk2---reading
2016-11-04 17:02:13.202 ThreadDemo[29492:5700972] blk1---reading
2016-11-04 17:02:13.203 ThreadDemo[29492:5700972] blk---writing
2016-11-04 17:02:13.203 ThreadDemo[29492:5700972] blk3---reading
2016-11-04 17:02:13.203 ThreadDemo[29492:5700974] blk4---reading
  • 使用Concurrent Dispatch Queuedispatch_barrier_async函數(shù)可實(shí)現(xiàn)高效率的數(shù)據(jù)庫(kù)訪問(wèn)和文件訪問(wèn),dispatch_barrier_async函數(shù)還是比較好理解的。

9. 循環(huán)執(zhí)行dispatch_apply

  • dispatch_apply()函數(shù)是用來(lái)循環(huán)來(lái)執(zhí)行隊(duì)列中的任務(wù)的,使用方式為:dispatch_apply(循環(huán)次數(shù), 任務(wù)所在的隊(duì)列) { 要循環(huán)執(zhí)行的任務(wù) }。使用該函數(shù)循環(huán)執(zhí)行并行隊(duì)列中的任務(wù)時(shí),會(huì)開(kāi)辟新的線程,不過(guò)有可能會(huì)在當(dāng)前線程中執(zhí)行一些任務(wù)。而使用dispatch_apply()執(zhí)行串行隊(duì)列中的任務(wù)時(shí),會(huì)在當(dāng)前線程中執(zhí)行。無(wú)論是使用并行隊(duì)列還是串行隊(duì)列,dispatch_apply()都會(huì)阻塞當(dāng)前執(zhí)行函數(shù)線程。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     dispatch_apply(10, queue, ^(size_t index){
     NSLog(@"%zu", index);
});
    
NSLog(@"done");
2016-11-04 17:24:25.819 ThreadDemo[29598:5715955] 2
2016-11-04 17:24:25.819 ThreadDemo[29598:5715904] 0
2016-11-04 17:24:25.819 ThreadDemo[29598:5716588] 1
2016-11-04 17:24:25.819 ThreadDemo[29598:5716640] 3
2016-11-04 17:24:25.820 ThreadDemo[29598:5715955] 4
2016-11-04 17:24:25.820 ThreadDemo[29598:5715904] 5
2016-11-04 17:24:25.820 ThreadDemo[29598:5716588] 6
2016-11-04 17:24:25.820 ThreadDemo[29598:5716640] 7
2016-11-04 17:24:25.820 ThreadDemo[29598:5715904] 9
2016-11-04 17:24:25.820 ThreadDemo[29598:5715955] 8
2016-11-04 17:24:25.820 ThreadDemo[29598:5715904] done
  • 輸出結(jié)果中最后的done必定在最后的位置上,這是因?yàn)閐ispatch_apply函數(shù)會(huì)等待全部處理執(zhí)行結(jié)束,也就是阻塞當(dāng)前執(zhí)行函數(shù)線程。

  • 另外,由于dispatch_apply函數(shù)也與dispatch_sync函數(shù)相同,會(huì)等待處理執(zhí)行結(jié)束(阻塞),因此推薦在dispatch_async函數(shù)中非同步地執(zhí)行dispatch_apply函數(shù)。

#pragma mark - Dispatch_apply
 - (void)GCDDispatchApplyMethod{
   
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    void(^blk1_reading)(void) = ^{
        NSLog(@"blk1---reading");
    };
    
    void(^blk2_reading)(void) = ^{
        NSLog(@"blk2---reading");
    };
    void(^blk3_reading)(void) = ^{
        NSLog(@"blk3---reading");
    };
    void(^blk_writing)(void) = ^{
        NSLog(@"blk---writing");
    };

    NSMutableArray *array = [NSMutableArray new];
    [array addObject:blk1_reading];
    [array addObject:blk2_reading];
    [array addObject:blk3_reading];
    [array addObject:blk_writing];
    
    dispatch_async(queue, ^{
        dispatch_apply(array.count, queue, ^(size_t index) {
            void (^blk)(void) = [array objectAtIndex:index];
            blk();
            NSLog(@"%zu====%@",index,[array objectAtIndex:index]);
        });
        NSLog(@"全部執(zhí)行結(jié)束");
        dispatch_async(dispatch_get_main_queue(), ^{
            //在main Dispatch queue中執(zhí)行處理,更新用戶界面等待
            NSLog(@"done");
        });
    });

}
打印結(jié)果:
2016-11-04 17:36:02.258 ThreadDemo[29635:5723967] blk3---reading
2016-11-04 17:36:02.258 ThreadDemo[29635:5723940] blk2---reading
2016-11-04 17:36:02.258 ThreadDemo[29635:5723929] blk1---reading
2016-11-04 17:36:02.258 ThreadDemo[29635:5723968] blk---writing
2016-11-04 17:36:02.259 ThreadDemo[29635:5723940] 1====<__NSGlobalBlock__: 0x102895af0>
2016-11-04 17:36:02.259 ThreadDemo[29635:5723967] 2====<__NSGlobalBlock__: 0x102895b30>
2016-11-04 17:36:02.259 ThreadDemo[29635:5723929] 0====<__NSGlobalBlock__: 0x102895ab0>
2016-11-04 17:36:02.259 ThreadDemo[29635:5723968] 3====<__NSGlobalBlock__: 0x102895b70>
2016-11-04 17:36:02.259 ThreadDemo[29635:5723940] 全部執(zhí)行結(jié)束
2016-11-04 17:36:02.259 ThreadDemo[29635:5723499] done

10. 隊(duì)列的掛起和喚醒

  • 隊(duì)列的掛起與喚醒相對(duì)較為簡(jiǎn)單,如果你想對(duì)一個(gè)隊(duì)列中的任務(wù)的執(zhí)行進(jìn)行掛起,那么你就使用dispatch_suspend()函數(shù)即可。如果你要喚醒某個(gè)掛起的隊(duì)列,那么你就可以使用dispatch_resum()函數(shù)。這兩個(gè)函數(shù)所需的參數(shù)都是你要掛起或者喚醒的隊(duì)列,鑒于知識(shí)點(diǎn)的簡(jiǎn)單性就不做過(guò)多的贅述了。
#pragma mark -Dispatch_suspend/Dispatch_resume
- (void)GCDDispatch_suspend_resume{
    //系統(tǒng)默認(rèn)生成的,所以無(wú)法調(diào)用dispatch_resume()和dispatch_suspend()來(lái)控制執(zhí)行繼續(xù)或中斷。
    
    dispatch_queue_t queue1 = dispatch_queue_create("queue1", 0);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", 0);
    
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_async(queue1, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"%@-------%d",[NSThread currentThread],i);
            sleep(1);         
        }
    });
    
    dispatch_async(queue2, ^{
        NSLog(@"task2");
    });
    
    dispatch_group_async(group, queue1, ^{
        NSLog(@"task1 finished!");
    });
    dispatch_group_async(group, queue2, ^{
       dispatch_suspend(queue1);//掛起
       NSLog(@"task2 finished!掛起queue1");
       [NSThread sleepForTimeInterval:20.0];
       dispatch_resume(queue1);//喚醒隊(duì)列
        
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    dispatch_async(queue1, ^{
        NSLog(@"task3");
    });
    dispatch_async(queue2, ^{
        NSLog(@"task4");
    });
}
打印結(jié)果:可以先思考一下打印結(jié)果
2016-11-04 17:59:41.219 ThreadDemo[29749:5738141] task2
2016-11-04 17:59:41.219 ThreadDemo[29749:5738138] <NSThread: 0x60000027c380>{number = 4, name = (null)}-------0
2016-11-04 17:59:41.220 ThreadDemo[29749:5738141] task2 finished!掛起queue1
2016-11-04 17:59:42.220 ThreadDemo[29749:5738138] <NSThread: 0x60000027c380>{number = 4, name = (null)}-------1
2016-11-04 17:59:43.223 ThreadDemo[29749:5738138] <NSThread: 0x60000027c380>{number = 4, name = (null)}-------2
2016-11-04 17:59:44.225 ThreadDemo[29749:5738138] <NSThread: 0x60000027c380>{number = 4, name = (null)}-------3
2016-11-04 17:59:45.230 ThreadDemo[29749:5738138] <NSThread: 0x60000027c380>{number = 4, name = (null)}-------4
2016-11-04 18:00:01.220 ThreadDemo[29749:5738141] task1 finished!
2016-11-04 18:00:01.220 ThreadDemo[29749:5738141] task3
2016-11-04 18:00:01.221 ThreadDemo[29749:5738769] task4

11. 信號(hào)量Dispatch Semaphore

當(dāng)并行執(zhí)行的處理更新數(shù)據(jù)時(shí),會(huì)產(chǎn)出數(shù)據(jù)不一致的情況,有時(shí)應(yīng)用就會(huì)異常退出??梢允褂肧erial Dispatch Queue 和 dispatch_barrier也可以處理這種情況,我們也可以使用信號(hào)量Dispatch Semaphor來(lái)更好的處理線程安全和同步的問(wèn)題。

Dispatch Semaphore是持有計(jì)數(shù)的信號(hào)。使用計(jì)數(shù)來(lái)實(shí)現(xiàn)該等待還是運(yùn)行功能。計(jì)數(shù)為0時(shí)等待,計(jì)數(shù)為1或者大于1時(shí),減去1而不是等待,就可以運(yùn)行。

其實(shí)信號(hào)量就是根據(jù)pv操作來(lái)實(shí)現(xiàn)的

  • p操作-1 v操作+1

信號(hào)量就是一個(gè)資源計(jì)數(shù)器,對(duì)信號(hào)量有兩個(gè)操作來(lái)達(dá)到互斥,分別是P和V操作。

一般情況是這樣進(jìn)行臨界訪問(wèn)或互斥訪問(wèn)的: 設(shè)信號(hào)量值為1, 當(dāng)一個(gè)進(jìn)程1運(yùn)行時(shí),使用資源,進(jìn)行P操作,即對(duì)信號(hào)量值減1,也就是資源數(shù)少了1個(gè)。這是信號(hào)量值為0。系統(tǒng)中規(guī)定當(dāng)信號(hào)量值為0是,必須等待,直到信號(hào)量值不為零才能繼續(xù)操作。 這時(shí)如果進(jìn)程2想要運(yùn)行,那么也必須進(jìn)行P操作,但是此時(shí)信號(hào)量為0,所以無(wú)法減1,即不能P操作,也就阻塞。這樣就到到了進(jìn)程1排他訪問(wèn)。 當(dāng)進(jìn)程1運(yùn)行結(jié)束后,釋放資源,進(jìn)行V操作。資源數(shù)重新加1,這是信號(hào)量的值變?yōu)?. 這時(shí)進(jìn)程2發(fā)現(xiàn)資源數(shù)不為0,信號(hào)量能進(jìn)行P操作了,立即執(zhí)行P操作。信號(hào)量值又變?yōu)?.次數(shù)進(jìn)程2咱有資源,排他訪問(wèn)資源。 這就是信號(hào)量來(lái)控制互斥的原理

信號(hào)量為0則阻塞線程,大于0則不會(huì)阻塞。則我們通過(guò)改變信號(hào)量的值,來(lái)控制是否阻塞線程,從而達(dá)到線程同步。

- (void)GCDDispatchSemaphore{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
  //創(chuàng)建一個(gè)信號(hào)量dispatch
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    
    NSMutableArray *array = [NSMutableArray new];
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            //一直等待信號(hào)量大于0
            dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, DISPATCH_TIME_FOREVER));
            
          //進(jìn)行排他控制的處理代碼...
          //將dispatch semphore的計(jì)數(shù)器減1 
            [array addObject:[NSNumber numberWithInteger:i]];
              
            //排他控制處理結(jié)束
            //將dispatch semphore的計(jì)數(shù)器加1    
            //如果有通過(guò)dispatch_semaphore_wait函數(shù)dispatch semphore的計(jì)數(shù)值增加的線程,等待就由最先等待的線程執(zhí)行                  
            dispatch_semaphore_signal(semaphore);
        });
        
        NSLog(@"%@",array);
    }
}
  • 創(chuàng)建一個(gè)信號(hào)量dispatch dispatch_semaphore_t dispatch_semaphore_create(long value);
  • 等待信號(hào)量longdispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);返回值和dispatch_group_wait函數(shù)相同,也可以通過(guò)下面進(jìn)行分支處理:
long  result = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10*NSEC_PER_SEC)));
            
 if (result == 0){
      //進(jìn)行排他控制的處理代碼...
      //將dispatch semphore的計(jì)數(shù)器減1 
      [array addObject:[NSNumber numberWithInteger:i]];
              
      //排他控制處理結(jié)束
      //將dispatch semphore的計(jì)數(shù)器加1    
      //如果有通過(guò)dispatch_semaphore_wait函數(shù)dispatch semphore的計(jì)數(shù)值增加的線程,等待就由最先等待的線程執(zhí)行                  
      dispatch_semaphore_signal(semaphore);
}else{
      //這個(gè)時(shí)候計(jì)數(shù)值為0,在達(dá)到指定時(shí)間為止還是等待狀態(tài)。
        //處理邏輯....
}

  • 在進(jìn)行排他處理結(jié)束時(shí),通過(guò)dispatch_semaphore_signal來(lái)講計(jì)數(shù)值加1

GCD總結(jié)

  1. 開(kāi)不開(kāi)線程由執(zhí)行任務(wù)的函數(shù)決定
  • 異步開(kāi),異步是多線程的代名詞
  • 同步不開(kāi)
  1. 開(kāi)幾條線程由隊(duì)列決定
  • 串行隊(duì)列開(kāi)一條線程(GCD會(huì)開(kāi)一條,NSOperation Queue最大并發(fā)數(shù)為1時(shí)也可能開(kāi)多條)
  • 并發(fā)隊(duì)列開(kāi)多條線程,具體能開(kāi)的線程數(shù)量由底層線程池決定
  1. GCD的使用步驟
  • 1.創(chuàng)建block任務(wù)
  • 2.創(chuàng)建GCD隊(duì)列(串行和并發(fā))
  • 3.把block任務(wù)放到GCD隊(duì)列里,以異步或者同步方式執(zhí)行GCD隊(duì)列

結(jié)尾:

能看到這里的小伙伴都是真愛(ài)啊,內(nèi)容太多了。學(xué)完這個(gè)你們多思考一下里面方法的聯(lián)系以及實(shí)現(xiàn),好了今天的GCD就講到這里,中間有什么錯(cuò)誤歡迎你們批評(píng)指出!下一篇博客將帶你走進(jìn)NSOperation。

喜歡的話就請(qǐng)點(diǎn)個(gè)喜歡加個(gè)關(guān)注_(樓主好無(wú)恥?。??

ThreadDemo下載鏈接

參考書(shū)籍:Objective-C高級(jí)編程iOS與OS X多線程和內(nèi)存管理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Dispatch Queues dispatch queues是執(zhí)行任務(wù)的強(qiáng)大工具,允許你同步或異步地執(zhí)行任意代碼...
    YangPu閱讀 706評(píng)論 0 4
  • NSThread 第一種:通過(guò)NSThread的對(duì)象方法 NSThread *thread = [[NSThrea...
    攻城獅GG閱讀 965評(píng)論 0 3
  • 歡迎大家指出文章中需要改正或者需要補(bǔ)充的地方,我會(huì)及時(shí)更新,非常感謝。 一. 多線程基礎(chǔ) 1. 進(jìn)程 進(jìn)程是指在系...
    xx_cc閱讀 7,392評(píng)論 11 70
  • 原創(chuàng)內(nèi)容,轉(zhuǎn)載請(qǐng)注明出處: http://www.itdecent.cn/p/ac11fe7ef78c 前言 多線...
    抱緊我的小鯉魚(yú)閱讀 8,901評(píng)論 6 78
  • 從哪說(shuō)起呢? 單純講多線程編程真的不知道從哪下嘴。。 不如我直接引用一個(gè)最簡(jiǎn)單的問(wèn)題,以這個(gè)作為切入點(diǎn)好了 在ma...
    Mr_Baymax閱讀 2,917評(píng)論 1 17

友情鏈接更多精彩內(nèi)容