【iOS】玩轉 - GCD

本文來自尚妝iOS團隊嘉文
發表於尚妝github博客,歡迎訂閱!git

GCD介紹


Grand Central Dispatch (GCD)是Apple開發的一個多核編程的解決方法
基於C語言,提供了很是多強大的函數github

術語


同步 (Synchronous)

在當前線程中執行任務,不具有開啓新線程的能力
提交的任務在執行完成後纔會返回
同步函數: dispatch_sync()objective-c

異步 (Asynchronous)

在新線程中執行任務,具有開啓新線程的能力
提交的任務馬上返回,在後臺隊列中執行
異步函數: dispatch_async()編程

串行 (Serial)

一個任務執行完畢後,再執行下一個任務安全

併發 (Concurrent)

多個任務同時執行(自動開啓多個線程),只有在異步函數下才有效併發

描述 說明
queue 隊列
main 主隊列
global 全局隊列
dispatch_queue_t 描述隊列
dispatch_block_t 描述任務
dispatch_once_t 描述一次性
dispatch_time_t 描述時間
dispatch_group_t 描述隊列組
dispatch_semaphore_t 描述信號量
函數 說明
dispatch_sync() 同步執行
dispatch_async() 異步執行
dispatch_after() 延時執行
dispatch_once() 一次性執行
dispatch_apply() 提交隊列
dispatch_queue_create() 建立隊列
dispatch_group_create() 建立隊列組
dispatch_group_async() 提交任務到隊列組
dispatch_group_enter() / dispatch_group_leave() 將隊列組中的任務未執行完畢的任務數目加減1(兩個函數要配合使用)
dispatch_group_notify() 監聽隊列組執行完畢
dispatch_group_wait() 設置等待時間(返回 0成功,1失敗)
注意:

1.全部的執行都放到隊列中(queue),隊列的特色是FIFO(先提交的先執行)
2.必須在主線程訪問 UIKit 的類
3.併發隊列只在異步函數下才有效app

#基本使用

NSLog(@"當前線程: %@", [NSThread currentThread]);
 //獲取主隊列
 dispatch_queue_t mainQueue = dispatch_get_main_queue();

 //獲取全局併發隊列
 dispatch_queue_t otherQueue =   dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

 //同步函數(在當前線程中執行,不具有開啓新線程的能力)
 dispatch_sync(otherQueue, ^{
      NSLog(@"同步 %@", [NSThread currentThread]);
 });

 //異步函數(在另外一條線程中執行,具有開啓新線程的能力)
 dispatch_async(otherQueue, ^{
      NSLog(@"異步 %@", [NSThread currentThread]);
 });

 //輸出:   當前線程: 
  
  
  

 
  
  {number = 1, name = main} //輸出: 同步 
 
  
    {number = 1, name = main} //輸出: 異步 
   
     {number = 3, name = (null)} 
    
   

 複製代碼
延時執行 dispatch_after()

dispatch_after()延遲一段時間把一項任務提交到隊列中執行,返回以後就不能取消
經常使用來在在主隊列上延遲執行一項任務異步

NSLog(@"當前線程 %@", [NSThread currentThread]);

    //GCD延時調用(主線程)(主隊列)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"GCD延時(主線程) %@", [NSThread currentThread]);
    });

    //GCD延時調用(其餘線程)(全局併發隊列)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"GCD延時(其餘線程) %@", [NSThread currentThread]);
    });

    //輸出:   當前線程 
  
  
  

 
  
  {number = 1, name = main} //輸出: GCD延時(主線程) 
 
  
    {number = 1, name = main} //輸出: GCD延時(其餘線程) 
   
     {number = 3, name = (null)} 
    
   

 複製代碼
一次性執行 dispatch_once()

整個程序運行中,只會執行一次 (默認線程是安全的)
dispatch_once() 以線程安全的方式執行且僅執行其代碼塊一次async

for (NSInteger i = 0; i < 10; i++) {

        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"GCD一次性執行(默認線程是安全的)");
        });

    }

 //輸出:   GCD一次性執行(默認線程是安全的)複製代碼
//使用GCD初始化單例
+ (instancetype)sharedManager { 

    static PhotoManager *sharedPhotoManager = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
        sharedPhotoManager = [[PhotoManager alloc] init]; 
    }); 

    return sharedPhotoManager; 
}複製代碼
提交 dispatch_apply()

把一項任務提交到隊列中屢次執行,具體是並行執行仍是串行執行由隊列自己決定
dispatch_apply不會馬上返回,在執行完畢後纔會返回,是同步的調用。函數

隊列

任務1,任務2依次執行,全部任務都執行成功後回到主線程
(效率不高)

NSLog(@"當前線程 %@", [NSThread currentThread]);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //全局併發隊列
        for (NSInteger i = 0; i < 5; i++) {
            NSLog(@"任務1 %@", [NSThread currentThread]);
        }

        for (NSInteger i = 0; i < 5; i++) {
            NSLog(@"任務2 %@", [NSThread currentThread]);
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            //主隊列
            NSLog(@"主線程執行(刷新UI) %@", [NSThread currentThread]);
        });

    });

    //輸出:   當前線程 
  
  
  

 
  
  {number = 1, name = main} //輸出: 任務1 
 
  
    {number = 3, name = (null)} //輸出: 任務1 
   
     {number = 3, name = (null)} //輸出: 任務1 
    
      {number = 3, name = (null)} //輸出: 任務1 
     
       {number = 3, name = (null)} //輸出: 任務1 
      
        {number = 3, name = (null)} //輸出: 任務2 
       
         {number = 3, name = (null)} //輸出: 任務2 
        
          {number = 3, name = (null)} //輸出: 任務2 
         
           {number = 3, name = (null)} //輸出: 任務2 
          
            {number = 3, name = (null)} //輸出: 任務2 
           
             {number = 3, name = (null)} //輸出: 任務2 
            
              {number = 3, name = (null)} //輸出: 主線程(刷新UI) 
             
               {number = 1, name = main} 
              
             
            
           
          
         
        
       
      
     
    
   

 複製代碼
隊列組

任務1,任務2同時執行,全部任務都執行成功後回到主線程
(效率高)

NSLog(@"當前線程 %@", [NSThread currentThread]);

    //(1)建立一個隊列組
    dispatch_group_t group= dispatch_group_create();

    //(2)開啓任務1
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        for (NSInteger i = 0; i < 5; i++) {
            NSLog(@"任務1 %@", [NSThread currentThread]);
        }

    });

    //(3)開啓任務2
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        for (NSInteger i = 0; i < 5; i++) {
            NSLog(@"任務2 %@", [NSThread currentThread]);
        }

    });

    //(4)全部任務執行完畢,回到主線程
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{

        NSLog(@"主線程(刷新UI) %@", [NSThread currentThread]);

    });

    //輸出:   當前線程 
  
  
  

 
  
  {number = 1, name = main} //輸出: 任務1 
 
  
    {number = 3, name = (null)} //輸出: 任務1 
   
     {number = 3, name = (null)} //輸出: 任務1 
    
      {number = 3, name = (null)} //輸出: 任務2 
     
       {number = 4, name = (null)} //輸出: 任務2 
      
        {number = 4, name = (null)} //輸出: 任務1 
       
         {number = 3, name = (null)} //輸出: 任務2 
        
          {number = 4, name = (null)} //輸出: 任務1 
         
           {number = 3, name = (null)} //輸出: 任務2 
          
            {number = 4, name = (null)} //輸出: 任務2 
           
             {number = 4, name = (null)} //輸出: 主線程(刷新UI) 
            
              {number = 1, name = main} 
             
            
           
          
         
        
       
      
     
    
   

 複製代碼

#串行與併發

各個隊列的執行效果

串行隊列

串行隊列

一個任務執行完畢後,再執行下一個任務
主隊列是GCD自帶的一種特殊的串行隊列,放在主隊列中的任務,都會放到主線程中執行

//(1)使用dispatch_queue_create函數建立串行隊列
    //參數1: 隊列名稱
 //參數2: 隊列屬性 (通常用NULL)
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);

    //(2)使用主隊列(跟主線程相關聯的隊列)
    dispatch_queue_t serialMainQueue = dispatch_get_main_queue();複製代碼
併發隊列

併發隊列

多個任務併發執行(自動開啓多個線程同時執行任務)

併發功能只有在異步(dispatch_async)函數下才有效!!!

GCD默認已經提供了全局的併發隊列,供整個應用使用,不須要手動建立

併發隊列優先級 快捷值 優先級
DISPATCH_QUEUE_PRIORITY_HIGH 2
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 中(默認)
DISPATCH_QUEUE_PRIORITY_LOW (-2)
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 後臺
//(1)使用dispatch_get_global_queue函數得到全局的併發隊列
    //參數1: 優先級
 //參數2: 暫時無用參數 (傳0)
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);複製代碼
異步函數_併發隊列

(開啓新線程,併發執行任務)

NSLog(@"當前線程 %@", [NSThread currentThread]);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任務1 %@", [NSThread currentThread]);
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任務2 %@", [NSThread currentThread]);
    });

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任務3 %@", [NSThread currentThread]);
    });

    //輸出:   當前線程 
  
  
  

 
  
  {number = 1, name = main} //輸出: 任務1 
 
  
    {number = 3, name = (null)} //輸出: 任務3 
   
     {number = 4, name = (null)} //輸出: 任務2 
    
      {number = 5, name = (null)} 
     
    
   

 複製代碼
異步函數_串行隊列

(開啓新線程,串行執行任務)

NSLog(@"當前線程 %@", [NSThread currentThread]);
    //建立串行隊列
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);

    dispatch_async(serialQueue, ^{
        NSLog(@"任務1 %@", [NSThread currentThread]);
    });

    dispatch_async(serialQueue, ^{
        NSLog(@"任務2 %@", [NSThread currentThread]);
    });

    dispatch_async(serialQueue, ^{
        NSLog(@"任務3 %@", [NSThread currentThread]);
    });

    //輸出:   當前線程 
  
  
  

 
  
  {number = 1, name = main} //輸出: 任務1 
 
  
    {number = 3, name = (null)} //輸出: 任務2 
   
     {number = 3, name = (null)} //輸出: 任務3 
    
      {number = 3, name = (null)} 
     
    
   

 複製代碼
同步函數_併發隊列

(不會開啓新線程,併發執行任務失效!)

NSLog(@"當前線程 %@", [NSThread currentThread]);
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任務1 %@", [NSThread currentThread]);
    });

    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任務2 %@", [NSThread currentThread]);
    });

    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任務3 %@", [NSThread currentThread]);
    });

    //輸出:   當前線程 
  
  
  

 
  
  {number = 1, name = main} //輸出: 任務1 
 
  
    {number = 1, name = main} //輸出: 任務2 
   
     {number = 1, name = main} //輸出: 任務3 
    
      {number = 1, name = main} 
     
    
   

 複製代碼
同步函數_串行隊列

(不會開啓新線程,串行執行任務)

NSLog(@"當前線程 %@", [NSThread currentThread]);
 //建立串行隊列
 dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
 dispatch_sync(serialQueue, ^{
     NSLog(@"任務1 %@", [NSThread currentThread]);
 });

 dispatch_sync(serialQueue, ^{
      NSLog(@"任務2 %@", [NSThread currentThread]);
 });

 dispatch_sync(serialQueue, ^{
     NSLog(@"任務3 %@", [NSThread currentThread]);
 });

 //輸出:   當前線程 
  
  
  

 
  
  {number = 1, name = main} //輸出: 任務1 
 
  
    {number = 1, name = main} //輸出: 任務2 
   
     {number = 1, name = main} //輸出: 任務3 
    
      {number = 1, name = main} 
     
    
   

 複製代碼
相關文章
相關標籤/搜索