iOS多線程(3)-GCD

  • 簡介:

    為什么要用 GCD 呢?因為 GCD 有很多好處啊,具體如下:
    GCD 可用于多核的并行運算
    GCD 會自動利用更多的 CPU 內核(比如雙核、四核)
    GCD 會自動管理線程的生命周期(創建線程、調度任務、銷毀線程)
    程序員只需要告訴 GCD 想要執行什么任務,不需要編寫任何線程管理代碼
    既然 GCD 有這么多的好處,那么下面我們就來系統的學習一下 GCD 的使用方法。

目錄:

  • 隊列創建

  • 同步異步

  • 線程間通訊

  • 延遲執行(dispatch_after)

  • 定時器(dispatch_timer)

  • 一次執行(dispatch_once)

  • 迭代(dispatch_apply)

  • 隊列組(dispatch_group_notify)

  • 等待(dispatch_group_wait)

  • 信號(dispatch_semaphore_t)

  • 并發控制通過信號實現

  • 隊列創建:
    //串行隊列
    dispatch_queue_t queue = dispatch_queue_create("com.gcd.peace", DISPATCH_QUEUE_SERIAL);
    //并行隊列
    dispatch_queue_t queue = dispatch_queue_create("com.gcd.peace", DISPATCH_QUEUE_CONCURRENT);
    //全局并行隊列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //主線程隊列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
  • 同步異步:
    - (void)asyncAction {
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      dispatch_async(queue, ^{
          NSLog(@"async---%@",[NSThread currentThread]);
      });
    }
    
    - (void)syncAction {
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      dispatch_sync(queue, ^{
          NSLog(@"sync---%@",[NSThread currentThread]);
      });
    }
    
    輸出:
    ---<NSThread: 0x60c00026bd40>{number = 5, name = (null)}
    ---<NSThread: 0x60000007f300>{number = 1, name = main}
    
  • 線程間通訊:
    - (void)asyncToSyncAction {
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          NSLog(@"---%@",[NSThread currentThread]);
          
          // 回到主線程
          dispatch_async(dispatch_get_main_queue(), ^{
              NSLog(@"---%@",[NSThread currentThread]);
          });
      });
    }
    
    輸出:
    ---<NSThread: 0x60c00026bd40>{number = 5, name = (null)}
    ---<NSThread: 0x60000007f300>{number = 1, name = main}
    
  • 柵欄(dispatch_barrier_async):
    - (void)barrierAction {
        dispatch_queue_t queue = dispatch_queue_create("com.gcd.peace", DISPATCH_QUEUE_CONCURRENT);
      
        for (int i = 0; i < 10; i ++) {
            dispatch_async(queue, ^{
                [NSThread sleepForTimeInterval:2];
                NSLog(@"1---%@",[NSThread currentThread]);
            });
        }
      
        //barrier
        dispatch_barrier_async(queue, ^{
            [NSThread sleepForTimeInterval:2];
            NSLog(@"[Barrier: ---%@",[NSThread currentThread]);
        });
    
        for (int i = 0; i < 10; i ++) {
            dispatch_async(queue, ^{
                [NSThread sleepForTimeInterval:2];
                NSLog(@"3---%@",[NSThread currentThread]);
            });
        }
    }
    
    輸出:
    2018-03-20 17:57:39.417137+0800 GCD[55856:2568834] 1---<NSThread: 0x604000074780>{number = 6, name = (null)}
    2018-03-20 17:57:39.417137+0800 GCD[55856:2568833] 1---<NSThread:   0x6000002701c0>{number = 3, name = (null)}
    2018-03-20 17:57:39.417138+0800 GCD[55856:2568832] 1---<NSThread: 0x60c00007e7c0>{number = 4, name = (null)}
    2018-03-20 17:57:39.417148+0800 GCD[55856:2568835] 1---<NSThread: 0x60800007d7c0>{number = 5, name = (null)}
    2018-03-20 17:57:39.417197+0800 GCD[55856:2568853] 1---<NSThread: 0x600000270080>{number = 7, name = (null)}
    2018-03-20 17:57:39.417247+0800 GCD[55856:2568855] 1---<NSThread: 0x6040000754c0>{number = 8, name = (null)}
    2018-03-20 17:57:39.417308+0800 GCD[55856:2568856] 1---<NSThread: 0x60000026fac0>{number = 10, name = (null)}
    2018-03-20 17:57:39.417329+0800 GCD[55856:2568854] 1---<NSThread: 0x604000074a80>{number = 9, name = (null)}
    2018-03-20 17:57:39.417344+0800 GCD[55856:2568857] 1---<NSThread: 0x604000074380>{number = 11, name = (null)}
    2018-03-20 17:57:39.417361+0800 GCD[55856:2568858] 1---<NSThread: 0x60c00007e800>{number = 12, name = (null)}
    2018-03-20 17:57:41.420468+0800 GCD[55856:2568858] [Barrier: ---<NSThread: 0x60c00007e800>{number = 12, name = (null)}
    2018-03-20 17:57:43.423862+0800 GCD[55856:2568858] 3---<NSThread: 0x60c00007e800>{number = 12, name = (null)}
    2018-03-20 17:57:43.423827+0800 GCD[55856:2568855] 3---<NSThread: 0x6040000754c0>{number = 8, name = (null)}
    2018-03-20 17:57:43.423826+0800 GCD[55856:2568854] 3---<NSThread: 0x604000074a80>{number = 9, name = (null)}
    2018-03-20 17:57:43.423827+0800 GCD[55856:2568857] 3---<NSThread: 0x604000074380>{number = 11, name = (null)}
    2018-03-20 17:57:43.423855+0800 GCD[55856:2568853] 3---<NSThread: 0x600000270080>{number = 7, name = (null)}
    2018-03-20 17:57:43.423823+0800 GCD[55856:2568856] 3---<NSThread: 0x60000026fac0>{number = 10, name = (null)}
    2018-03-20 17:57:43.423949+0800 GCD[55856:2568834] 3---<NSThread: 0x604000074780>{number = 6, name = (null)}
    2018-03-20 17:57:43.423952+0800 GCD[55856:2568833] 3---<NSThread: 0x6000002701c0>{number = 3, name = (null)}
    2018-03-20 17:57:43.423960+0800 GCD[55856:2568835] 3---<NSThread: 0x60800007d7c0>{number = 5, name = (null)}
    2018-03-20 17:57:43.423964+0800 GCD[55856:2568832] 3---<NSThread: 0x60c00007e7c0>{number = 4, name = (null)}
    
  • 延遲執行(dispatch_after):
    - (void)afterAction {
      NSLog(@"[After]: begin currentThread---%@",[NSThread currentThread]);
      
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
          NSLog(@"[After]: main ---%@",[NSThread currentThread]);
      });
      
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          NSLog(@"[After]: global ---%@",[NSThread currentThread]);
      });
    }
    
    輸出:
    2018-03-20 18:01:33.749444+0800 GCD[55856:2568766] [After]: begin currentThread---<NSThread: 0x60000006e280>{number = 1, name = main}
    2018-03-20 18:01:35.928257+0800 GCD[55856:2568766] [After]: main ---<NSThread: 0x60000006e280>{number = 1, name = main}
    2018-03-20 18:01:35.928274+0800 GCD[55856:2571380] [After]: global ---<NSThread: 0x60c00007e280>{number = 13, name = (null)}
    
  • 定時器(dispatch_timer):
    @property(nonatomic,strong) dispatch_source_t tTimer;
    
    - (void)timerAction {
     NSLog(@"[Timer]: begin currentThread---%@",[NSThread currentThread]);
     
     if (!self.tTimer) {
         self.tTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
         NSLog(@"self.tTimer = %@",self.tTimer);
         dispatch_source_set_timer(self.tTimer,
                                   dispatch_walltime(NULL, 0 * NSEC_PER_SEC),
                                   0.32 * NSEC_PER_SEC,
                                   0);
         dispatch_source_set_event_handler(self.tTimer, ^{
             NSLog(@"1111");
         });
     
         dispatch_resume(self.tTimer);
     }
     else {
         dispatch_source_cancel(self.tTimer);
         self.tTimer = nil;
     }
     //        dispatch_suspend(self.tTimer);
     //        dispatch_source_cancel(self.tTimer);
     //        self.tTimer = nil; Crash
     
     //        dispatch_suspend(self.tTimer);
     //        self.tTimer = nil; Crash
    }
    
  • 一次執行(dispatch_once):
    - (void)onceAction {
      __block int index = 0;
      for (int i = 0; i < 10; i ++) {
          static dispatch_once_t onceToken;
          dispatch_once(&onceToken, ^{
              index ++;
              NSLog(@"[Once]:-------index = %d",index);
          });
      }
    }
    
    輸出:
    2018-03-20 18:03:17.189632+0800 GCD[55856:2568766] [Once]:-------index = 1
    
  • 迭代(dispatch_apply):
    - (void)applyAction {
      //并行迭代
      dispatch_queue_t queue = dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      //串行隊列迭代與for循環效果一樣
      //queue = dispatch_queue_t queue = dispatch_queue_create("com.gcd.peace", DISPATCH_QUEUE_SERIAL);
      
      NSLog(@"apply---begin");
      dispatch_apply(6, queue, ^(size_t index) {
          NSLog(@"%zd---%@",index, [NSThread currentThread]);
          [NSThread sleepForTimeInterval:2];
      });
      NSLog(@"apply---end");
    }
    
    并行隊列輸出:
    2018-03-20 18:05:42.458703+0800 GCD[56041:2574450] apply---begin
    2018-03-20 18:05:42.459384+0800 GCD[56041:2574450] 0---<NSThread: 0x60c000262bc0>{number = 1, name = main}
    2018-03-20 18:05:42.459417+0800 GCD[56041:2574512] 1---<NSThread: 0x60c00027f840>{number = 3, name = (null)}
    2018-03-20 18:05:42.459504+0800 GCD[56041:2574499] 4---<NSThread: 0x6080002786c0>{number = 7, name = (null)}
    2018-03-20 18:05:42.459504+0800 GCD[56041:2574502] 3---<NSThread: 0x604000270e00>{number = 4, name = (null)}
    2018-03-20 18:05:42.459506+0800 GCD[56041:2574500] 5---<NSThread: 0x604000270c00>{number = 6, name = (null)}
    2018-03-20 18:05:42.459521+0800 GCD[56041:2574501] 2---<NSThread: 0x608000278a00>{number = 5, name = (null)}
    2018-03-20 18:05:44.463743+0800 GCD[56041:2574450] apply---end
    
    串行隊列輸出:
    2018-03-20 18:07:33.988048+0800 GCD[56083:2576141] apply---begin
    2018-03-20 18:07:33.988516+0800 GCD[56083:2576141] 0---<NSThread: 0x60c000065740>{number = 1, name = main}
    2018-03-20 18:07:35.989109+0800 GCD[56083:2576141] 1---<NSThread: 0x60c000065740>{number = 1, name = main}
    2018-03-20 18:07:37.990556+0800 GCD[56083:2576141] 2---<NSThread: 0x60c000065740>{number = 1, name = main}
    2018-03-20 18:07:39.992050+0800 GCD[56083:2576141] 3---<NSThread: 0x60c000065740>{number = 1, name = main}
    2018-03-20 18:07:41.993583+0800 GCD[56083:2576141] 4---<NSThread: 0x60c000065740>{number = 1, name = main}
    2018-03-20 18:07:43.995163+0800 GCD[56083:2576141] 5---<NSThread: 0x60c000065740>{number = 1, name = main}
    2018-03-20 18:07:45.995811+0800 GCD[56083:2576141] apply---end
    
  • 隊列組(dispatch_group_notify):
    - (void)groupNotifyAction {
      NSLog(@"group---begin");
      
      dispatch_group_t group = dispatch_group_create();
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      
      dispatch_group_async(group, queue, ^{
          [NSThread sleepForTimeInterval:2];
          NSLog(@"1---%@",[NSThread currentThread]);
      });
      
      dispatch_group_async(group, queue, ^{
          [NSThread sleepForTimeInterval:2];
          NSLog(@"2---%@",[NSThread currentThread]);
      });
      
      dispatch_group_notify(group, [self mainQueue], ^{
          [NSThread sleepForTimeInterval:2];
          NSLog(@"3---%@",[NSThread currentThread]);
          NSLog(@"group notify");
      });
    }
    
    輸出:
    2018-03-20 18:09:47.306064+0800 GCD[56083:2576141] group---begin
    2018-03-20 18:09:49.307609+0800 GCD[56083:2576199] 2---<NSThread: 0x60800006ce00>{number = 4, name = (null)}
    2018-03-20 18:09:49.307608+0800 GCD[56083:2577770] 1---<NSThread: 0x60400007fac0>{number = 3, name = (null)}
    2018-03-20 18:09:51.309380+0800 GCD[56083:2576141] 3---<NSThread: 0x60c000065740>{number = 1, name = main}
    2018-03-20 18:09:51.309744+0800 GCD[56083:2576141] group notify
    
    - (void)groupNotify1Action {
      NSLog(@"group---begin");
      
      dispatch_group_t group = dispatch_group_create();
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      
      dispatch_group_enter(group);
      dispatch_async(queue, ^{
          [NSThread sleepForTimeInterval:2];
          NSLog(@"1---%@",[NSThread currentThread]);
          dispatch_group_leave(group);
      });
      
      dispatch_group_enter(group);
      dispatch_async(queue, ^{
          [NSThread sleepForTimeInterval:5];
          NSLog(@"2---%@",[NSThread currentThread]);
          dispatch_group_leave(group);
      });
      
      dispatch_group_notify(group, [self mainQueue], ^{
          NSLog(@"group notify : %@",[NSThread currentThread]);
      });
    }
    
    輸出:
    2018-03-20 18:09:53.329762+0800 GCD[56083:2576141] group---begin
    2018-03-20 18:09:55.332737+0800 GCD[56083:2576199] 1---<NSThread: 0x60800006ce00>{number = 4, name = (null)}
    2018-03-20 18:09:58.331259+0800 GCD[56083:2577770] 2---<NSThread: 0x60400007fac0>{number = 3, name = (null)}
    2018-03-20 18:09:58.331676+0800 GCD[56083:2576141] group notify : <NSThread: 0x60c000065740>{number = 1, name = main}
    
  • 等待(dispatch_group_wait):
    - (void)groupWaitAction {
      NSLog(@"group---begin");
      
      dispatch_group_t group =  dispatch_group_create();
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      
      dispatch_group_async(group, queue, ^{
          [NSThread sleepForTimeInterval:2];
          NSLog(@"1---%@",[NSThread currentThread]);
      });
      
      dispatch_group_async(group, queue, ^{
          [NSThread sleepForTimeInterval:5];
          NSLog(@"2---%@",[NSThread currentThread]);
      });
      
      dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
      
      NSLog(@"group---end");
    }
    
    輸出:
    2018-03-20 18:12:57.523759+0800 GCD[56083:2576141] group---begin
    2018-03-20 18:12:59.527048+0800 GCD[56083:2579780] 1---<NSThread: 0x600000260780>{number = 5, name = (null)}
    2018-03-20 18:13:02.524335+0800 GCD[56083:2577781] 2---<NSThread: 0x60000007fd40>{number = 6, name = (null)}
    2018-03-20 18:13:02.524954+0800 GCD[56083:2576141] group---end
    
  • 信號(dispatch_semaphore_t):
    //通過信號實現同步功能
    - (void)semaphoreAction {
      NSLog(@"semaphore---begin");
      
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
      
      dispatch_async(queue, ^{
          [NSThread sleepForTimeInterval:2];
          NSLog(@"semaphore --- %@",[NSThread currentThread]);
          
          dispatch_semaphore_signal(semaphore);
      });
      
      dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//信號量=0則阻擋,>0則通過
      NSLog(@"semaphore---end");
    }
    
    輸出:
    2018-03-20 18:14:28.265856+0800 GCD[56083:2576141] semaphore---begin
    2018-03-20 18:14:30.271145+0800 GCD[56083:2580996] semaphore --- <NSThread: 0x60c000078880>{number = 8, name = (null)}
    2018-03-20 18:14:30.271384+0800 GCD[56083:2576141] semaphore---end
    
    //通過信號實現鎖
    - (void)semaphore1Action {
      dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
      dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
      
      for (int i = 0; i < 100; i++) {
          dispatch_async(queue, ^{
              // 相當于加鎖
              dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
              NSLog(@"i = %zd semaphore = %@", i, semaphore);
              // 相當于解鎖
              dispatch_semaphore_signal(semaphore);
          });
      }
    }
    
    輸出:
    2018-03-20 18:16:21.561341+0800 GCD[56217:2582806] i = 0 semaphore = <OS_dispatch_semaphore: 0x60c000084510>
    2018-03-20 18:16:21.561717+0800 GCD[56217:2582805] i = 1 semaphore = <OS_dispatch_semaphore: 0x60c000084510>
    2018-03-20 18:16:21.561999+0800 GCD[56217:2582803] i = 2 semaphore = <OS_dispatch_semaphore: 0x60c000084510>
    2018-03-20 18:16:21.562277+0800 GCD[56217:2582804] i = 3 semaphore = <OS_dispatch_semaphore: 0x60c000084510>
    2018-03-20 18:16:21.562508+0800 GCD[56217:2582812] i = 4 semaphore = <OS_dispatch_semaphore: 0x60c000084510>
    2018-03-20 18:16:21.562745+0800 GCD[56217:2582823] i = 5 semaphore = <OS_dispatch_semaphore: 0x60c000084510>
    2018-03-20 18:16:21.562979+0800 GCD[56217:2582824] i = 6 semaphore = <OS_dispatch_semaphore: 0x60c000084510>
    2018-03-20 18:16:21.563213+0800 GCD[56217:2582825] i = 7 semaphore = <OS_dispatch_semaphore: 0x60c000084510>
    2018-03-20 18:16:21.563442+0800 GCD[56217:2582826] i = 8 semaphore = <OS_dispatch_semaphore: 0x60c000084510>
    2018-03-20 18:16:21.563671+0800 GCD[56217:2582827] i = 9 semaphore = <OS_dispatch_semaphore: 0x60c000084510>
    
  • 并發控制通過信號實現:
    void dispatch_async_limit(dispatch_queue_t queue,NSUInteger limitSemaphoreCount, dispatch_block_t block) {
      //控制并發數的信號量
      static dispatch_semaphore_t limitSemaphore;
      
      //專門控制并發等待的線程
      static dispatch_queue_t receiverQueue;
      
      //使用 dispatch_once而非 lazy 模式,防止可能的多線程搶占問題
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
          limitSemaphore = dispatch_semaphore_create(limitSemaphoreCount);
          receiverQueue = dispatch_queue_create("receiver", DISPATCH_QUEUE_SERIAL);
      });
      
      // 如不加 receiverQueue 放在主線程會阻塞主線程
      dispatch_async(receiverQueue, ^{
          //可用信號量后才能繼續,否則等待
          dispatch_semaphore_wait(limitSemaphore, DISPATCH_TIME_FOREVER);
          dispatch_async(queue, ^{
              !block ? : block();
              //在該工作線程執行完成后釋放信號量
              long semaphore = dispatch_semaphore_signal(limitSemaphore);
              if (semaphore > 0) {
                  NSLog(@"\n");
              }
          });
      });
    }
    
    - (void)limitAction {
      for (int i = 0; i < 10; i++) {
          dispatch_async_limit([self serialQueue],3, ^{
              sleep(2);
              NSLog(@"------i = %d",i);
          });
      }
    }
    
    輸出:
    2018-03-20 17:33:45.367914+0800 GCD[55534:2553299] ------i = 1
    2018-03-20 17:33:45.367914+0800 GCD[55534:2553303] ------i = 0
    2018-03-20 17:33:45.367916+0800 GCD[55534:2553300] ------i = 2
    2018-03-20 17:33:45.368276+0800 GCD[55534:2553303] 
    2018-03-20 17:33:47.373465+0800 GCD[55534:2553300] ------i = 3
    2018-03-20 17:33:47.373482+0800 GCD[55534:2553299] ------i = 4
    2018-03-20 17:33:47.373496+0800 GCD[55534:2553302] ------i = 5
    2018-03-20 17:33:47.373856+0800 GCD[55534:2553300] 
    2018-03-20 17:33:49.379032+0800 GCD[55534:2553302] ------i = 6
    2018-03-20 17:33:49.379048+0800 GCD[55534:2553299] ------i = 7
    2018-03-20 17:33:49.379048+0800 GCD[55534:2553303] ------i = 8
    2018-03-20 17:33:49.379452+0800 GCD[55534:2553299] 
    2018-03-20 17:33:51.383480+0800 GCD[55534:2553301] ------i = 9
    
  • 源碼:

    代碼在這里

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 本文首發于我的個人博客:「程序員充電站」[https://itcharge.cn]文章鏈接:「傳送門」[https...
    ITCharge閱讀 349,059評論 308 1,926
  • 一、前言 上一篇文章iOS多線程淺匯-原理篇中整理了一些有關多線程的基本概念。本篇博文介紹的是iOS中常用的幾個多...
    nuclear閱讀 2,074評論 6 18
  • 1. GCD簡介 什么是GCD呢?我們先來看看百度百科的解釋簡單了解下概念 引自百度百科:Grand Centra...
    千尋_544f閱讀 402評論 0 0
  • 看到過不少文章描繪出同窗好友多年后重聚的場景,總是淚眼婆娑,感嘆良多,心存戚戚,我以為這次見到Y后也會如此。 Y是...
    頌世梵歌閱讀 454評論 0 2
  • 1.帶實習生上課,聲音太小,講太多,學生不太買賬。 接下來,還是自己來上課吧。進度不能落后。 2.實習生買了水果,...
    Alian__閱讀 226評論 0 0