玩轉(zhuǎn) GCD

玩轉(zhuǎn) GCD

GCD介紹


Grand Central Dispatch (GCD)是Apple開發(fā)的一個多核編程的解決方法
基于C語言,提供了非常多強大的函數(shù)
GCD和其他多線程技術(shù)方案,如NSThread,NSOperationQueue,NSInvocationOperation等相比,使用起來更加方便。

術(shù)語


同步 (Synchronous)

在當前線程中執(zhí)行任務,不具備開啟新線程的能力
提交的任務在執(zhí)行完成后才會返回
同步函數(shù): dispatch_sync()

異步 (Asynchronous)

在新線程中執(zhí)行任務,具備開啟新線程的能力
提交的任務立刻返回,在后臺隊列中執(zhí)行
異步函數(shù): dispatch_async()

串行 (Serial)

一個任務執(zhí)行完畢后,再執(zhí)行下一個任務

并發(fā) (Concurrent)

多個任務同時執(zhí)行(自動開啟多個線程),只有在異步函數(shù)下才有效

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

1.所有的執(zhí)行都放到隊列中(queue),隊列的特點是FIFO(先提交的先執(zhí)行)
2.必須在主線程訪問 UIKit 的類
3.并發(fā)隊列只在異步函數(shù)下才有效

基本使用


 NSLog(@"當前線程: %@", [NSThread currentThread]);
 //獲取主隊列
 dispatch_queue_t mainQueue = dispatch_get_main_queue();
 
 //獲取全局并發(fā)隊列
 dispatch_queue_t otherQueue =   dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
 //同步函數(shù)(在當前線程中執(zhí)行,不具備開啟新線程的能力)
 dispatch_sync(otherQueue, ^{
      NSLog(@"同步 %@", [NSThread currentThread]);
 });
    
 //異步函數(shù)(在另一條線程中執(zhí)行,具備開啟新線程的能力)
 dispatch_async(otherQueue, ^{
      NSLog(@"異步 %@", [NSThread currentThread]);
 });
    
 //輸出:   當前線程: <NSThread: 0x7f81bac06670>{number = 1, name = main}
 //輸出:   同步 <NSThread: 0x7f81bac06670>{number = 1, name = main}
 //輸出:   異步 <NSThread: 0x7f81bae35be0>{number = 3, name = (null)}
延時執(zhí)行 dispatch_after()

dispatch_after()延遲一段時間把一項任務提交到隊列中執(zhí)行,返回之后就不能取消
常用來在在主隊列上延遲執(zhí)行一項任務

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

    //GCD延時調(diào)用(主線程)(主隊列)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"GCD延時(主線程) %@", [NSThread currentThread]);
    });
    
    //GCD延時調(diào)用(其他線程)(全局并發(fā)隊列)
    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)}
一次性執(zhí)行 dispatch_once()

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

    for (NSInteger i = 0; i < 10; i++) {
      
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"GCD一次性執(zhí)行(默認線程是安全的)");
        });
        
    }

 //輸出:   GCD一次性執(zhí)行(默認線程是安全的)
//使用GCD初始化單例
+ (instancetype)sharedManager { 
  
    static PhotoManager *sharedPhotoManager = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
        sharedPhotoManager = [[PhotoManager alloc] init]; 
    }); 

    return sharedPhotoManager; 
} 
提交 dispatch_apply()

把一項任務提交到隊列中多次執(zhí)行,具體是并行執(zhí)行還是串行執(zhí)行由隊列本身決定
dispatch_apply不會立刻返回,在執(zhí)行完畢后才會返回,是同步的調(diào)用。

隊列

任務1,任務2依次執(zhí)行,所有任務都執(zhí)行成功后回到主線程
(效率不高)

 NSLog(@"當前線程 %@", [NSThread currentThread]);
   
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //全局并發(fā)隊列
        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(@"主線程執(zhí)行(刷新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同時執(zhí)行,所有任務都執(zhí)行成功后回到主線程
(效率高)

    NSLog(@"當前線程 %@", [NSThread currentThread]);
    
    //(1)創(chuàng)建一個隊列組
    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)所有任務執(zhí)行完畢,回到主線程
    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}

串行與并發(fā)


各個隊列的執(zhí)行效果
串行隊列
串行隊列

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

    //(1)使用dispatch_queue_create函數(shù)創(chuàng)建串行隊列
    //參數(shù)1: 隊列名稱
 //參數(shù)2: 隊列屬性 (一般用NULL)
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
    
    //(2)使用主隊列(跟主線程相關(guān)聯(lián)的隊列)
    dispatch_queue_t serialMainQueue = dispatch_get_main_queue();
并發(fā)隊列
并發(fā)隊列

多個任務并發(fā)執(zhí)行(自動開啟多個線程同時執(zhí)行任務)

<u>并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效!!!</u>

GCD默認已經(jīng)提供了全局的并發(fā)隊列,供整個應用使用,不需要手動創(chuàng)建

并發(fā)隊列優(yōu)先級 快捷值 優(yōu)先級
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函數(shù)獲得全局的并發(fā)隊列
    //參數(shù)1: 優(yōu)先級
 //參數(shù)2: 暫時無用參數(shù) (傳0)
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
異步函數(shù)_并發(fā)隊列

(開啟新線程,并發(fā)執(zhí)行任務)

    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)}
異步函數(shù)_串行隊列

(開啟新線程,串行執(zhí)行任務)

    NSLog(@"當前線程 %@", [NSThread currentThread]);
    //創(chuàng)建串行隊列
    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)}
同步函數(shù)_并發(fā)隊列

(不會開啟新線程,并發(fā)執(zhí)行任務失效!)

    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}
同步函數(shù)_串行隊列

(不會開啟新線程,串行執(zhí)行任務)

 NSLog(@"當前線程 %@", [NSThread currentThread]);
 //創(chuàng)建串行隊列
 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}

多個網(wǎng)絡請求示例

- (void)exampleMoreNetwork{
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t serialQueue = dispatch_queue_create("magic_gcd_group", DISPATCH_QUEUE_SERIAL);
    
    // 網(wǎng)絡請求1
    dispatch_group_enter(group);
    dispatch_group_async(group, serialQueue, ^{
        [[MagicNetworkManager shareManager] GET:@"網(wǎng)絡請求1" Parameters:nil Success:^(NSURLResponse *response, id responseObject) {
            dispatch_group_leave(group);
        } Failure:^(NSURLResponse *response, id error) {
            dispatch_group_leave(group);
        }];
    });
    
    // 網(wǎng)絡請求2
    dispatch_group_enter(group);
    dispatch_group_async(group, serialQueue, ^{
        [[MagicNetworkManager shareManager] GET:@"網(wǎng)絡請求2" Parameters:nil Success:^(NSURLResponse *response, id responseObject) {
            dispatch_group_leave(group);
        } Failure:^(NSURLResponse *response, id error) {
            dispatch_group_leave(group);
        }];
    });
    
    // 所有網(wǎng)絡請求結(jié)束
    dispatch_group_notify(group, serialQueue, ^{
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            dispatch_async(dispatch_get_main_queue(), ^{
                // 主線程刷新UI
            });
        });
    });
    
}

多張圖片上傳示例

- (void)networkUploadWithURLString:(NSString *)URLString images:(NSMutableArray *)images {
    
    NSMutableArray *result = [NSMutableArray array];
    for (NSInteger i = 0; i < images.count; i++) {
        [result addObject:[NSNull null]];
    }
    
    dispatch_group_t group = dispatch_group_create();
    
    for (NSInteger i = 0; i < images.count; i++) {
        
        dispatch_group_enter(group);
        
        //1. 圖片存放地址
        NSString *url = URLString;
        //2. 圖片名稱自定義
        NSString *imageName = [[NSDate date] formattedDateWithFormat:@"yyyyMMddHHmmss"];
        NSString *fileName = [NSString stringWithFormat:@"%@_%ld.png",imageName,i];
        //3. 圖片二進制文件
        NSData *imageData = UIImagePNGRepresentation(images[i]);
        //NSLog(@"文件大小: %ld k", (long)(imageData.length / 1024));
        //4. 發(fā)起網(wǎng)絡請求
        AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
        manager.requestSerializer = [AFJSONRequestSerializer serializer];
        manager.responseSerializer = [AFJSONResponseSerializer serializer];
        manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain", @"application/json", @"text/html", nil];
        [manager POST:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
            [formData appendPartWithFileData:imageData name:@"file" fileName:fileName mimeType:@"image/png"];
        } progress:^(NSProgress * _Nonnull uploadProgress) {
            
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            
            NSLog(@"第 %d 張圖片上傳成功: %@", (int)i + 1, responseObject);
            // NSMutableArray 是線程不安全的,所以加個同步鎖
            @synchronized (result) {
                result[i] = responseObject;
            }
            dispatch_group_leave(group);
            
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            
            NSLog(@"第 %d 張圖片上傳失敗: %@", (int)i + 1, error);
            dispatch_group_leave(group);
            
        }];
        
    }
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"上傳完成!");
        for (id response in result) {
            NSLog(@"%@", response);
        }
    });
}

推薦閱讀

GCD 深入理解(一)
GCD 深入理解(二)
關(guān)于iOS多線程淺析

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

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

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