【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(先提交的先執行)app

2.必須在主線程訪問 UIKit 的類
3.併發隊列只在異步函數下才有效異步

基本使用


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]);
 });
    
 //輸出:   當前線程: <NSThread: 0x7f81bac06670>{number = 1, name = main}
 //輸出:   同步 <NSThread: 0x7f81bac06670>{number = 1, name = main}
 //輸出:   異步 <NSThread: 0x7f81bae35be0>{number = 3, name = (null)}
延時執行 dispatch_after()

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

    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]);
    });

    //輸出:   當前線程 <NSThread: 0x7f8cb9701990>{number = 1, name = main}
    //輸出:   GCD延時(主線程) <NSThread: 0x7f8cb9701990>{number = 1, name = main}
    //輸出:   GCD延時(其餘線程) <NSThread: 0x7f8cb9513ee0>{number = 3, name = (null)}
一次性執行 dispatch_once()

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

    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]);
        });
        
    });
    
    //輸出:   當前線程 <NSThread: 0x7fa78040b8b0>{number = 1, name = main}
    //輸出:   任務1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //輸出:   任務1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //輸出:   任務1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //輸出:   任務1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //輸出:   任務1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //輸出:   任務2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //輸出:   任務2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //輸出:   任務2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //輸出:   任務2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //輸出:   任務2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //輸出:   任務2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //輸出:   主線程(刷新UI) <NSThread: 0x7f9da1705940>{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]);
        
    });
    
    //輸出:   當前線程 <NSThread: 0x7fd951704e70>{number = 1, name = main}
    //輸出:   任務1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
    //輸出:   任務1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
    //輸出:   任務1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
    //輸出:   任務2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
    //輸出:   任務2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
    //輸出:   任務1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
    //輸出:   任務2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
    //輸出:   任務1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
    //輸出:   任務2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
    //輸出:   任務2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
    //輸出:   主線程(刷新UI) <NSThread: 0x7ff65a406fb0>{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();
併發隊列

併發隊列

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

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

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]);
    });
    
    //輸出:   當前線程 <NSThread: 0x7fbe83c067c0>{number = 1, name = main}
    //輸出:   任務1 <NSThread: 0x7fb84160ed90>{number = 3, name = (null)}
    //輸出:   任務3 <NSThread: 0x7fb841752940>{number = 4, name = (null)}
    //輸出:   任務2 <NSThread: 0x7fb8414167b0>{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]);
    });
    
    //輸出:   當前線程 <NSThread: 0x7fc192e05380>{number = 1, name = main}
    //輸出:   任務1 <NSThread: 0x7fc192c16670>{number = 3, name = (null)}
    //輸出:   任務2 <NSThread: 0x7fc192c16670>{number = 3, name = (null)}
    //輸出:   任務3 <NSThread: 0x7fc192c16670>{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]);
    });
    
    //輸出:   當前線程 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main}
    //輸出:   任務1 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main}
    //輸出:   任務2 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main}
    //輸出:   任務3 <NSThread: 0x7fa0dbf02e20>{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]);
 });

 //輸出:   當前線程 <NSThread: 0x7f8c0b407f00>{number = 1, name = main}
 //輸出:   任務1 <NSThread: 0x7f8c0b407f00>{number = 1, name = main}
 //輸出:   任務2 <NSThread: 0x7f8c0b407f00>{number = 1, name = main}
 //輸出:   任務3 <NSThread: 0x7f8c0b407f00>{number = 1, name = main}
相關文章
相關標籤/搜索