iOS 多线程 GCD(一)

前言

本文主要接扫GCD的基本使用方法,比如创建并发异步任务、比如创建线程组等内容。

1、GCD的简介

此处省略1万字......

代码的下载地址demo;

2、 GCD 的基本使用

首先我们简单的举个例子,模拟下网络请求的回掉,代码如下。

typedef void(^comletionHandler)(NSString *status);

/**
 模拟网络请求的回掉
 
 @param comletionHandler 模拟网络请求回掉
 */
- (void)netWorkingComletionHandler:(comletionHandler)comletionHandler{
    // 异步子线程 网络请求耗时操作
        NSInteger sleepTimeInterval = [self getRandomNumber:1 to:5];
        NSString *randomNumber = [NSString stringWithFormat:@"当前线程是:%@ 随机等待:%ld秒",[NSThread currentThread],sleepTimeInterval];
        [NSThread sleepForTimeInterval:sleepTimeInterval];
        // 异步主线程 网络请求回掉请求内容
        dispatch_async(dispatch_get_main_queue(), ^{
            
            if (comletionHandler) {
                comletionHandler(randomNumber);
            }
        });
    });
}

我们在使用GCD执行一个线程任务的时候,其实只有两步:

  • 创建一个队列(串行队列或并发队列)
  • 将任务追加到任务的等待队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)

2.1、创建一个队列(串行队列或并发队列)

2.1.1 dispatch_queue_create创建
// 串行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

$dispatch_queue_create 创建方法 dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);

  • 参数 label 是一个C语言的字符串,标识这给队列
  • 参数 attr 识别这个参数是串行队列还是并发队列,只有 DISPATCH_QUEUE_SERIAL(串行)DISPATCH_QUEUE_CONCURRENT(并行) 两个选择 。
2.1.2 dispatch_get_main_queue()获取主队列
// 主队列的获取方法
dispatch_queue_t queue = dispatch_get_main_queue();

这个是一个串行主队列,也就是我们常说的主线程

2.1.3 dispatch_get_global_queue()获取全局并发队列
// 全局并发队列的获取方法
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

这个是一个串行全局队列,也就是我们常说的子线程
创建方法 dispatch_get_global_queue(long identifier, unsigned long flags);

  • 参数 identifier 表示队列的优先级,具体的值如下,意义与字面意思一致。
#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
  • 参数 flags 暂时没有任何意义。

2.2、创建任务

GCD 提供了同步执行任务的创建方法和异步执行任务创建方法。

// 同步执行任务创建方法
dispatch_sync(queue, ^{
    // 这里放同步执行任务代码
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
    // 这里放异步执行任务代码
});

2.3、线程队列组合

虽然使用 GCD 只需两步,但是既然我们有两种队列(串行队列/并发队列),两种任务执行方式(同步执行/异步执行),那么我们就有了四种不同的组合方式。这四种不同的组合方式是:

组合 同步(sync) 异步 (async)
并行(concurrent) 没有开启新线程,当前线程中,串行执行任务 如线程充裕会开启新的1个线程,若线程不充裕等待线程空闲后,并行执行任务
串行(serial) 没有开启新线程,当前线程中,串行执行任务 如线程充裕会开启新的1个线程,若线程不充裕等待线程空闲后,串行执行任务
主线程 主线程调用:死锁卡住不执行 主线程中,串行执行任务

3、GCD的基本使用

如下是模拟网络请求回掉的代码

#pragma mark -
#pragma mark -  模拟网络请求回掉
/**
 模拟网络请求的回掉
 
 @param comletionHandler 模拟网络请求回掉
 */
- (void)netWorkingComletionHandler:(comletionHandler)comletionHandler{
    // 异步子线程 网络请求耗时操作
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSInteger sleepTimeInterval = [self getRandomNumber:1 to:5];
        NSString *randomNumber = [NSString stringWithFormat:@"当前线程是:%@ 随机等待:%ld秒",[NSThread currentThread],sleepTimeInterval];
        [NSThread sleepForTimeInterval:sleepTimeInterval];
        // 异步主线程 网络请求回掉请求内容
        dispatch_async(dispatch_get_main_queue(), ^{
            if (comletionHandler) {
                comletionHandler(randomNumber);
            }
        });
    });
}

/**
 获取随机数
 
 @param from 随机数最小值
 @param to 随机数最大值
 @return 最小值与最小值之间的随机数
 */
- (NSInteger)getRandomNumber:(NSInteger)from to:(NSInteger)to {
    return (NSInteger)(from + (arc4random() % (to - from + 1)));
}

3.1、同步执行Sync + 并发队列Concurrent

#pragma mark -
#pragma mark -  同步执行sync + 并发队列CONCURRENT
- (void)syncConcurrentTask{
    
    NSLog(@"当前线程是:%@ syncConcurrentTask---开始",[NSThread currentThread]);  // 打印当前线程
    dispatch_queue_t syncQueue = dispatch_queue_create("syncQueuex", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(syncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第一并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第一并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
        
    });
    
    dispatch_sync(syncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第二并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第二并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    
    dispatch_sync(syncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第三并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第三并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    NSLog(@"当前线程是:%@ syncConcurrentTask---结束",[NSThread currentThread]);  // 打印当前线程
}

打印结果如下

同步执行sync + 并发队列CONCURRENT

2019-04-02 14:13:02.295615+0800 GCDDemo[1687:198762] 当前线程是:{number = 1, name = main} syncConcurrentTask---开始
2019-04-02 14:13:02.295832+0800 GCDDemo[1687:198762] 第一并发 -0- 当前线程是:{number = 1, name = main}
2019-04-02 14:13:02.296023+0800 GCDDemo[1687:198762] 第一并发 -1- 当前线程是:{number = 1, name = main}
2019-04-02 14:13:02.296226+0800 GCDDemo[1687:198762] 第二并发 -0- 当前线程是:{number = 1, name = main}
2019-04-02 14:13:02.296404+0800 GCDDemo[1687:198762] 第二并发 -1- 当前线程是:{number = 1, name = main}
2019-04-02 14:13:02.296611+0800 GCDDemo[1687:198762] 第三并发 -0- 当前线程是:{number = 1, name = main}
2019-04-02 14:13:02.296814+0800 GCDDemo[1687:198762] 第三并发 -1- 当前线程是:{number = 1, name = main}
2019-04-02 14:13:02.297006+0800 GCDDemo[1687:198762] 当前线程是:{number = 1, name = main} syncConcurrentTask---结束
2019-04-02 14:13:03.299666+0800 GCDDemo[1687:198762] 第三并发 -1- 发回掉结束 当前线程是:{number = 18, name = (null)} 随机等待:1秒
2019-04-02 14:13:04.300250+0800 GCDDemo[1687:198762] 第一并发 -0- 发回掉结束 当前线程是:{number = 9, name = (null)} 随机等待:2秒
2019-04-02 14:13:05.301669+0800 GCDDemo[1687:198762] 第二并发 -0- 发回掉结束 当前线程是:{number = 15, name = (null)} 随机等待:3秒
2019-04-02 14:13:06.301919+0800 GCDDemo[1687:198762] 第二并发 -1- 发回掉结束 当前线程是:{number = 16, name = (null)} 随机等待:4秒
2019-04-02 14:13:07.299635+0800 GCDDemo[1687:198762] 第三并发 -0- 发回掉结束 当前线程是:{number = 17, name = (null)} 随机等待:5秒
2019-04-02 14:13:07.299891+0800 GCDDemo[1687:198762] 第一并发 -1- 发回掉结束 当前线程是:{number = 14, name = (null)} 随机等待:5秒
  • 主线程开启整个任务,同步执行的并发任务均在主线程中添加开启。
  • 所有的同步任务均在 任务 开始结束 中执行。
  • 任务在 开始结束 中顺序执行,全部在主线程中顺序等待执行。
  • 并发未开启多个线程,所有同步执行的并发任务均在主线程中。
  • 网络请求回掉均在子线程中执行,耗时操作后回主线程收到回掉。

3.2、异步执行async + 并发队列CONCURRENT

- (void)asyncConcurrentTask{
    
    NSLog(@"当前线程是:%@ asyncConcurrentTask---开始",[NSThread currentThread]);  // 打印当前线程
    dispatch_queue_t asyncQueue = dispatch_queue_create("asyncQueuex", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(asyncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第一并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第一并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    dispatch_async(asyncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第二并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第二并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    dispatch_async(asyncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第三并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第三并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    NSLog(@"当前线程是:%@ asyncConcurrentTask---结束",[NSThread currentThread]);  // 打印当前线程
}

打印结果

异步执行async + 并发队列CONCURRENT

2019-04-02 14:27:36.434638+0800 GCDDemo[1687:198762] 当前线程是:{number = 1, name = main} asyncConcurrentTask---开始
2019-04-02 14:27:36.434864+0800 GCDDemo[1687:198762] 当前线程是:{number = 1, name = main} asyncConcurrentTask---结束
2019-04-02 14:27:36.434918+0800 GCDDemo[1687:294549] 第一并发 -0- 当前线程是:{number = 16, name = (null)}
2019-04-02 14:27:36.435030+0800 GCDDemo[1687:309754] 第二并发 -0- 当前线程是:{number = 19, name = (null)}
2019-04-02 14:27:36.435085+0800 GCDDemo[1687:309755] 第三并发 -0- 当前线程是:{number = 20, name = (null)}
2019-04-02 14:27:36.435169+0800 GCDDemo[1687:294549] 第一并发 -1- 当前线程是:{number = 16, name = (null)}
2019-04-02 14:27:36.435301+0800 GCDDemo[1687:309755] 第三并发 -1- 当前线程是:{number = 20, name = (null)}
2019-04-02 14:27:36.435311+0800 GCDDemo[1687:309754] 第二并发 -1- 当前线程是:{number = 19, name = (null)}
2019-04-02 14:27:37.436217+0800 GCDDemo[1687:198762] 第二并发 -0- 发回掉结束 当前线程是:{number = 22, name = (null)} 随机等待:1秒
2019-04-02 14:27:38.436826+0800 GCDDemo[1687:198762] 第一并发 -0- 发回掉结束 当前线程是:{number = 21, name = (null)} 随机等待:2秒
2019-04-02 14:27:39.436183+0800 GCDDemo[1687:198762] 第三并发 -0- 发回掉结束 当前线程是:{number = 23, name = (null)} 随机等待:3秒
2019-04-02 14:27:41.440393+0800 GCDDemo[1687:198762] 第三并发 -1- 发回掉结束 当前线程是:{number = 20, name = (null)} 随机等待:5秒
2019-04-02 14:27:41.440653+0800 GCDDemo[1687:198762] 第二并发 -1- 发回掉结束 当前线程是:{number = 19, name = (null)} 随机等待:5秒
2019-04-02 14:27:41.440829+0800 GCDDemo[1687:198762] 第一并发 -1- 发回掉结束 当前线程是:{number = 16, name = (null)} 随机等待:5秒

  • 主线程异步执行的并发任务由主线程开启以后,全部在子线程创建执行。
  • 所有的同步任务均不在 开始结束 中执行
  • 任务非顺序执行, 开始结束 以后才执行。
  • 并发开启了多个子线程。
  • 网络请求回掉均在子线程中执行,耗时操作后回主线程收到回掉。

3.3、同步执行sync + 串行队列Serial

- (void)syncSerialTask{
    
    NSLog(@"当前线程是:%@ syncSerialTask---开始",[NSThread currentThread]);  // 打印当前线程
    dispatch_queue_t syncQueue = dispatch_queue_create("syncQueuex", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(syncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第一并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第一并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    dispatch_sync(syncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第二并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第二并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    dispatch_sync(syncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第三并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第三并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    NSLog(@"当前线程是:%@ syncSerialTask---结束",[NSThread currentThread]);  // 打印当前线程
}

打印结果

同步执行sync + 串行队列Serial

2019-04-02 14:34:40.460881+0800 GCDDemo[1687:198762] 当前线程是:{number = 1, name = main} syncSerialTask---开始
2019-04-02 14:34:40.461027+0800 GCDDemo[1687:198762] 第一并发 -0- 当前线程是:{number = 1, name = main}
2019-04-02 14:34:40.461159+0800 GCDDemo[1687:198762] 第一并发 -1- 当前线程是:{number = 1, name = main}
2019-04-02 14:34:40.461311+0800 GCDDemo[1687:198762] 第二并发 -0- 当前线程是:{number = 1, name = main}
2019-04-02 14:34:40.461470+0800 GCDDemo[1687:198762] 第二并发 -1- 当前线程是:{number = 1, name = main}
2019-04-02 14:34:40.461602+0800 GCDDemo[1687:198762] 第三并发 -0- 当前线程是:{number = 1, name = main}
2019-04-02 14:34:40.461734+0800 GCDDemo[1687:198762] 第三并发 -1- 当前线程是:{number = 1, name = main}
2019-04-02 14:34:40.461879+0800 GCDDemo[1687:198762] 当前线程是:{number = 1, name = main} syncSerialTask---结束
2019-04-02 14:34:41.464691+0800 GCDDemo[1687:198762] 第一并发 -1- 发回掉结束 当前线程是:{number = 24, name = (null)} 随机等待:1秒
2019-04-02 14:34:41.464922+0800 GCDDemo[1687:198762] 第二并发 -0- 发回掉结束 当前线程是:{number = 25, name = (null)} 随机等待:1秒
2019-04-02 14:34:41.465097+0800 GCDDemo[1687:198762] 第三并发 -0- 发回掉结束 当前线程是:{number = 27, name = (null)} 随机等待:1秒
2019-04-02 14:34:41.465276+0800 GCDDemo[1687:198762] 第二并发 -1- 发回掉结束 当前线程是:{number = 26, name = (null)} 随机等待:1秒
2019-04-02 14:34:42.464661+0800 GCDDemo[1687:198762] 第三并发 -1- 发回掉结束 当前线程是:{number = 28, name = (null)} 随机等待:2秒
2019-04-02 14:34:44.464535+0800 GCDDemo[1687:198762] 第一并发 -0- 发回掉结束 当前线程是:{number = 16, name = (null)} 随机等待:4秒
  • 同步并发 的结果完全一致
  • 未开启新的线程,主线程同步执行的并发任务全部在主线程开启。
  • 任务顺序执行,全部在主线程中顺序等待执行。
  • 所有的同步任务均在 开始结束 中执行
  • 并发未开启多个线程。
  • 网络请求回掉均在子线程中执行,耗时操作后回主线程收到回掉。

3.4、同步执行sync + 串行队列Serial


#pragma mark -
#pragma mark -  异步执行async + 串行队列Serial
- (void)asyncSerialTask{
    
    NSLog(@"当前线程是:%@ asyncSerialTask---开始",[NSThread currentThread]);  // 打印当前线程
    dispatch_queue_t asyncQueue = dispatch_queue_create("asyncQueuex", DISPATCH_QUEUE_SERIAL);
    dispatch_async(asyncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第一并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第一并发 -%d- 发回掉结束 %@",i,status);
            }];
            
        };
    });
    
    dispatch_async(asyncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第二并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第二并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    dispatch_async(asyncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第三并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第三并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    NSLog(@"当前线程是:%@ asyncSerialTask---结束",[NSThread currentThread]);  // 打印当前线程
}

打印结果

异步执行async + 串行队列Serial

2019-04-02 14:42:08.988744+0800 GCDDemo[1687:198762] 当前线程是:{number = 1, name = main} asyncSerialTask---开始
2019-04-02 14:42:08.988989+0800 GCDDemo[1687:198762] 当前线程是:{number = 1, name = main} asyncSerialTask---结束
2019-04-02 14:42:08.989040+0800 GCDDemo[1687:317379] 第一并发 -0- 当前线程是:{number = 25, name = (null)}
2019-04-02 14:42:08.989260+0800 GCDDemo[1687:317379] 第一并发 -1- 当前线程是:{number = 25, name = (null)}
2019-04-02 14:42:08.989539+0800 GCDDemo[1687:317379] 第二并发 -0- 当前线程是:{number = 25, name = (null)}
2019-04-02 14:42:08.989784+0800 GCDDemo[1687:317379] 第二并发 -1- 当前线程是:{number = 25, name = (null)}
2019-04-02 14:42:08.990045+0800 GCDDemo[1687:317379] 第三并发 -0- 当前线程是:{number = 25, name = (null)}
2019-04-02 14:42:08.990256+0800 GCDDemo[1687:317379] 第三并发 -1- 当前线程是:{number = 25, name = (null)}
2019-04-02 14:42:10.989722+0800 GCDDemo[1687:198762] 第一并发 -0- 发回掉结束 当前线程是:{number = 29, name = (null)} 随机等待:2秒
2019-04-02 14:42:10.990055+0800 GCDDemo[1687:198762] 第一并发 -1- 发回掉结束 当前线程是:{number = 30, name = (null)} 随机等待:2秒
2019-04-02 14:42:10.990432+0800 GCDDemo[1687:198762] 第三并发 -0- 发回掉结束 当前线程是:{number = 33, name = (null)} 随机等待:2秒
2019-04-02 14:42:10.990673+0800 GCDDemo[1687:198762] 第三并发 -1- 发回掉结束 当前线程是:{number = 34, name = (null)} 随机等待:2秒
2019-04-02 14:42:11.991086+0800 GCDDemo[1687:198762] 第二并发 -0- 发回掉结束 当前线程是:{number = 31, name = (null)} 随机等待:3秒
2019-04-02 14:42:12.994463+0800 GCDDemo[1687:198762] 第二并发 -1- 发回掉结束 当前线程是:{number = 32, name = (null)} 随机等待:4秒
  • 开启了一个子线程,主线程异步执行的并发任务全部在子线程开启。
  • 任务顺序执行,全部在主线程中顺序等待执行。
  • 所有的异步任务均不在 开始结束 中执行。
  • 开启了一个子线程,所有异步创建的任务均在这个子线程中顺序执行。
  • 网络请求回掉均在子线程中执行,耗时操作后回主线程收到回掉。

3.5、异步执行async + 主线程


#pragma mark -
#pragma mark -  异步执行async + 主线程
- (void)asyncMainThreadTask{
    
    NSLog(@"当前线程是:%@ asyncMainThreadTask---开始",[NSThread currentThread]);  // 打印当前线程
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_async(mainQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第一并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第一并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    dispatch_async(mainQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第二并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第二并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    dispatch_async(mainQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第三并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第三并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    NSLog(@"当前线程是:%@ asyncMainThreadTask---结束",[NSThread currentThread]);  // 打印当前线程
}


打印结果

异步执行sync + 主线程

2019-04-02 15:35:17.420403+0800 GCDDemo[2068:378102] 当前线程是:{number = 1, name = main} asyncMainThreadTask---开始
2019-04-02 15:35:17.420681+0800 GCDDemo[2068:378102] 当前线程是:{number = 1, name = main} asyncMainThreadTask---结束
2019-04-02 15:35:17.421753+0800 GCDDemo[2068:378102] 第一并发 -0- 当前线程是:{number = 1, name = main}
2019-04-02 15:35:17.421900+0800 GCDDemo[2068:378102] 第一并发 -1- 当前线程是:{number = 1, name = main}
2019-04-02 15:35:17.422021+0800 GCDDemo[2068:378102] 第二并发 -0- 当前线程是:{number = 1, name = main}
2019-04-02 15:35:17.422173+0800 GCDDemo[2068:378102] 第二并发 -1- 当前线程是:{number = 1, name = main}
2019-04-02 15:35:17.422309+0800 GCDDemo[2068:378102] 第三并发 -0- 当前线程是:{number = 1, name = main}
2019-04-02 15:35:17.422470+0800 GCDDemo[2068:378102] 第三并发 -1- 当前线程是:{number = 1, name = main}
2019-04-02 15:35:18.425564+0800 GCDDemo[2068:378102] 第二并发 -0- 发回掉结束 当前线程是:{number = 5, name = (null)} 随机等待:1秒
2019-04-02 15:35:19.426342+0800 GCDDemo[2068:378102] 第三并发 -0- 发回掉结束 当前线程是:{number = 7, name = (null)} 随机等待:2秒
2019-04-02 15:35:19.426601+0800 GCDDemo[2068:378102] 第三并发 -1- 发回掉结束 当前线程是:{number = 8, name = (null)} 随机等待:2秒
2019-04-02 15:35:19.426794+0800 GCDDemo[2068:378102] 第一并发 -0- 发回掉结束 当前线程是:{number = 3, name = (null)} 随机等待:2秒
2019-04-02 15:35:21.425182+0800 GCDDemo[2068:378102] 第一并发 -1- 发回掉结束 当前线程是:{number = 4, name = (null)} 随机等待:4秒
2019-04-02 15:35:22.425825+0800 GCDDemo[2068:378102] 第二并发 -1- 发回掉结束 当前线程是:{number = 6, name = (null)} 随机等待:5秒
  • 串行并发

3.6、同步执行sync + 主线程


#pragma mark -
#pragma mark -  同步执行sync + 主线程
- (void)syncMainThreadTask{
    NSLog(@"当前线程是:%@ syncMainThreadTask---开始",[NSThread currentThread]);  // 打印当前线程
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_sync(mainQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第一并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第一并发 -%d- 发回掉结束 %@",i,status);
            }];
            
        };
    });
    
    dispatch_sync(mainQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第二并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第二并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    dispatch_sync(mainQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第三并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第三并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    NSLog(@"当前线程是:%@ syncMainThreadTask---结束",[NSThread currentThread]);  // 打印当前线程
}

打印结果

同步执行sync + 主线程

2019-04-02 15:38:59.040983+0800 GCDDemo[2068:378102] 当前线程是:{number = 1, name = main} syncMainThreadTask---开始
(lldb) 
  • 会崩溃。死锁
  • 崩溃原因
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
  • 死锁分析 首先执行任务1,这是肯定没问题的,只是接下来,程序遇到了同步线程,那么它会进入等待,等待任务2执行完,然后执行任务3。但这是队列,有任务来,当然会将任务加到队尾,然后遵循FIFO原则执行任务。那么,现在任务2就会被加到最后,任务3排在了任务2前面,问题来了:任务3要等任务2执行完才能执行,任务2由排在任务3后面,意味着任务2要在任务3执行完才能执行,所以他们进入了互相等待的局面。这就是死锁。

4、 GCD 栅栏方法:dispatch_barrier_async

我们有时需要异步执行两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于栅栏一样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到dispatch_barrier_async方法在两个操作组间形成栅栏。

dispatch_barrier_async函数会等待前边追加到并发队列中的任务全部执行完毕之后,再将指定的任务追加到该异步队列中。然后在dispatch_barrier_async函数追加的任务执行完毕之后,异步队列才恢复为一般动作,接着追加任务到该异步队列并开始执行。

代码如下

#pragma mark -
#pragma mark - 栅栏方法
- (void)asyncBarrierTask{
    NSLog(@"当前线程是:%@ asyncBarrierTask---开始",[NSThread currentThread]);  // 打印当前线程
    dispatch_queue_t asyncQueue = dispatch_queue_create("asyncQueuex", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(asyncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第一并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第一并发 -%d- 发回掉结束 %@",i,status);
            }];
            
        };
    });
    
    dispatch_async(asyncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第二并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第二并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    dispatch_barrier_async(asyncQueue, ^{
        // 追加任务 barrier
        for (int i = 0; i < 2; i++) {
            NSLog(@"barrier -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"barrier -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    dispatch_async(asyncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第三并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第三并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    dispatch_async(asyncQueue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第四并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第四并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    NSLog(@"当前线程是:%@ asyncBarrierTask---结束",[NSThread currentThread]);  // 打印当前线程
}

打印结果

异步执行sync + 栅栏Barrier

2019-04-02 15:59:36.216231+0800 GCDDemo[2299:421705] 当前线程是:{number = 1, name = main} asyncBarrierTask---开始
2019-04-02 15:59:36.216535+0800 GCDDemo[2299:421705] 当前线程是:{number = 1, name = main} asyncBarrierTask---结束
2019-04-02 15:59:36.216590+0800 GCDDemo[2299:421760] 第一并发 -0- 当前线程是:{number = 3, name = (null)}
2019-04-02 15:59:36.216619+0800 GCDDemo[2299:421762] 第二并发 -0- 当前线程是:{number = 4, name = (null)}
2019-04-02 15:59:36.216841+0800 GCDDemo[2299:421762] 第二并发 -1- 当前线程是:{number = 4, name = (null)}
2019-04-02 15:59:36.216849+0800 GCDDemo[2299:421760] 第一并发 -1- 当前线程是:{number = 3, name = (null)}
2019-04-02 15:59:36.217116+0800 GCDDemo[2299:421765] barrier -0- 当前线程是:{number = 7, name = (null)}
2019-04-02 15:59:36.217297+0800 GCDDemo[2299:421765] barrier -1- 当前线程是:{number = 7, name = (null)}
2019-04-02 15:59:36.217577+0800 GCDDemo[2299:421897] 第三并发 -0- 当前线程是:{number = 9, name = (null)}
2019-04-02 15:59:36.217591+0800 GCDDemo[2299:421898] 第四并发 -0- 当前线程是:{number = 10, name = (null)}
2019-04-02 15:59:36.222329+0800 GCDDemo[2299:421897] 第三并发 -1- 当前线程是:{number = 9, name = (null)}
2019-04-02 15:59:36.222329+0800 GCDDemo[2299:421898] 第四并发 -1- 当前线程是:{number = 10, name = (null)}
2019-04-02 15:59:37.219824+0800 GCDDemo[2299:421705] 第二并发 -0- 发回掉结束 当前线程是:{number = 6, name = (null)} 随机等待:1秒
2019-04-02 15:59:37.223322+0800 GCDDemo[2299:421705] 第四并发 -1- 发回掉结束 当前线程是:{number = 9, name = (null)} 随机等待:1秒
2019-04-02 15:59:38.220144+0800 GCDDemo[2299:421705] barrier -1- 发回掉结束 当前线程是:{number = 7, name = (null)} 随机等待:2秒
2019-04-02 15:59:38.224344+0800 GCDDemo[2299:421705] 第三并发 -0- 发回掉结束 当前线程是:{number = 11, name = (null)} 随机等待:2秒
2019-04-02 15:59:39.217972+0800 GCDDemo[2299:421705] 第一并发 -1- 发回掉结束 当前线程是:{number = 4, name = (null)} 随机等待:3秒
2019-04-02 15:59:39.218202+0800 GCDDemo[2299:421705] 第一并发 -0- 发回掉结束 当前线程是:{number = 5, name = (null)} 随机等待:3秒
2019-04-02 15:59:39.224897+0800 GCDDemo[2299:421705] 第四并发 -0- 发回掉结束 当前线程是:{number = 12, name = (null)} 随机等待:3秒
2019-04-02 15:59:40.220113+0800 GCDDemo[2299:421705] barrier -0- 发回掉结束 当前线程是:{number = 8, name = (null)} 随机等待:4秒
2019-04-02 15:59:41.221252+0800 GCDDemo[2299:421705] 第二并发 -1- 发回掉结束 当前线程是:{number = 3, name = (null)} 随机等待:5秒
2019-04-02 15:59:41.225311+0800 GCDDemo[2299:421705] 第三并发 -1- 发回掉结束 当前线程是:{number = 10, name = (null)} 随机等待:5秒
  • 如果没有 dispatch_barrier_async,应该打印结果会按照异步并发的打印结果来执行
  • 如果有 dispatch_barrier_asyncdispatch_barrier_async任务之前的所有并发任务必须全部发起执行后,再发起执行dispatch_barrier_async中的任务,最后发起执行dispatch_barrier_async后的任务。
  • 但是每个模拟网络请求任务的回掉不在dispatch_barrier_async的控制范围,所以,dispatch_barrier_async只是控制了任务的发起,并不关系并发任务的回掉内容。

5、 GCD 延时执行:dispatch_after

我们经常会遇到这样的需求:在指定时间(例如3秒)之后执行某个任务。可以用 GCD 的dispatch_after函数来实现。
需要注意的是:dispatch_after函数并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after函数是很有效的。

#pragma mark -
#pragma mark - 延时等待
- (void)excuteGCDAfterMehod{
    NSLog(@"当前线程是:%@ excuteAfterMehod---开始",[NSThread currentThread]);  // 打印当前线程
    // 子线程等待3秒
    dispatch_time_t globalQueueTimeAfter = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_after(globalQueueTimeAfter, globalQueue, ^{
        NSLog(@"当前线程是:%@ excuteAfterMehod---子线程继续",[NSThread currentThread]);  // 打印当前线程
        // 主线程等待1秒
        dispatch_time_t mainQueueTimeAfter = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        dispatch_after(mainQueueTimeAfter, mainQueue, ^{
            NSLog(@"当前线程是:%@ excuteAfterMehod---主线程继续",[NSThread currentThread]);  // 打印当前线程
        });
    });
}

打印结果

2019-04-02 16:24:05.623341+0800 GCDDemo[2485:452952] 延时执行
2019-04-02 16:24:05.623582+0800 GCDDemo[2485:452952] 当前线程是:{number = 1, name = main} excuteAfterMehod---开始
2019-04-02 16:24:08.624034+0800 GCDDemo[2485:453017] 当前线程是:{number = 3, name = (null)} excuteAfterMehod---子线程继续
2019-04-02 16:24:09.711925+0800 GCDDemo[2485:452952] 当前线程是:{number = 1, name = main} excuteAfterMehod---主线程继续
  • 从打印结果来看,第一个回掉任务 是在子线程中加入,延时等待了3秒
  • 第二个回掉任务是在主线程中加入,延时等待了1秒

6、GCD 队列组:dispatch_group

有时候我们会有这样的应用场景:分别异步执行多个耗时任务,然后当多个耗时任务都执行完毕后再回到主线程执行任务。这时候我们可以用到 GCD 的队列组。

目前实现上述需求的有三个解决方案dispatch_group_notifydispatch_group_waitdispatch_group_enter+dispatch_group_leave方案。

6.1、方案1:dispatch_group_notify

#pragma mark -
#pragma mark -  dispatch_group_notify

- (void)asyncGroupNotifyTask{
    
    NSLog(@"当前线程是:%@ asyncGroupNotifyTask---开始",[NSThread currentThread]);  // 打印当前线程
    dispatch_group_t group =  dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第一并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第一并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第二并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第二并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    NSLog(@"当前线程是:%@ asyncGroupNotifyTask---继续",[NSThread currentThread]);  // 打印当前线程
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第三并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第三并发 -%d- 发回掉结束 %@",i,status);
            }];
            NSLog(@"当前线程是:%@ asyncGroupNotifyTask---结束",[NSThread currentThread]);  // 打印当前线程
        };
    });
    
}

打印结果

2019-04-02 17:26:46.398977+0800 GCDDemo[2999:527321] GroupNotify
2019-04-02 17:26:46.399196+0800 GCDDemo[2999:527321] 当前线程是:{number = 1, name = main} asyncGroupNotifyTask---开始
2019-04-02 17:26:46.399346+0800 GCDDemo[2999:527321] 当前线程是:{number = 1, name = main} asyncGroupNotifyTask---继续
2019-04-02 17:26:46.399372+0800 GCDDemo[2999:527374] 第二并发 -0- 当前线程是:{number = 4, name = (null)}
2019-04-02 17:26:46.399386+0800 GCDDemo[2999:527373] 第一并发 -0- 当前线程是:{number = 3, name = (null)}
2019-04-02 17:26:46.399784+0800 GCDDemo[2999:527374] 第二并发 -1- 当前线程是:{number = 4, name = (null)}
2019-04-02 17:26:46.399788+0800 GCDDemo[2999:527373] 第一并发 -1- 当前线程是:{number = 3, name = (null)}
2019-04-02 17:26:46.400116+0800 GCDDemo[2999:527321] 第三并发 -0- 当前线程是:{number = 1, name = main}
2019-04-02 17:26:46.400218+0800 GCDDemo[2999:527321] 第三并发 -1- 当前线程是:{number = 1, name = main}
2019-04-02 17:26:46.400345+0800 GCDDemo[2999:527321] 当前线程是:{number = 1, name = main} asyncGroupNotifyTask---结束
2019-04-02 17:26:47.403711+0800 GCDDemo[2999:527321] 第二并发 -0- 发回掉结束 当前线程是:{number = 5, name = (null)} 随机等待:1秒
2019-04-02 17:26:48.405119+0800 GCDDemo[2999:527321] 第二并发 -1- 发回掉结束 当前线程是:{number = 4, name = (null)} 随机等待:2秒
2019-04-02 17:26:49.400737+0800 GCDDemo[2999:527321] 第三并发 -1- 发回掉结束 当前线程是:{number = 8, name = (null)} 随机等待:3秒
2019-04-02 17:26:50.401832+0800 GCDDemo[2999:527321] 第一并发 -1- 发回掉结束 当前线程是:{number = 3, name = (null)} 随机等待:4秒
2019-04-02 17:26:51.402809+0800 GCDDemo[2999:527321] 第一并发 -0- 发回掉结束 当前线程是:{number = 6, name = (null)} 随机等待:5秒
2019-04-02 17:26:51.403048+0800 GCDDemo[2999:527321] 第三并发 -0- 发回掉结束 当前线程是:{number = 7, name = (null)} 随机等待:5秒

  • 从打印几个来看,dispatch_group_notify能保证我们的目标操作一定能在我们的前两个并发的耗时操作之后执行dispatch_group_notify回掉中的任务。
  • 不会阻塞当前线程,因为目标等待任务可以在都是dispatch_group_notify回掉中的执行
  • 无法控制回掉并发任务的回掉。

6.2、方案2:dispatch_group_wait


#pragma mark -
#pragma mark -  dispatch_group_wait
- (void)asyncGroupWaitTask{
    
    NSLog(@"当前线程是:%@ asyncGroupWaitTask---开始",[NSThread currentThread]);  // 打印当前线程
    dispatch_group_t group =  dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第一并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第一并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"第二并发 -%d- 当前线程是:%@",i,[NSThread currentThread]);  // 打印当前线程
            [self netWorkingComletionHandler:^(NSString *status) {
                NSLog(@"第二并发 -%d- 发回掉结束 %@",i,status);
            }];
        };
    });
    
    NSLog(@"当前线程是:%@ asyncGroupWaitTask---继续",[NSThread currentThread]);  // 打印当前线程
    // 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    NSLog(@"当前线程是:%@ asyncGroupWaitTask---结束",[NSThread currentThread]);  // 打印当前线程
}

打印结果

2019-04-02 17:27:27.189132+0800 GCDDemo[2999:527321] GroupWait
2019-04-02 17:27:27.189417+0800 GCDDemo[2999:527321] 当前线程是:{number = 1, name = main} asyncGroupWaitTask---开始
2019-04-02 17:27:27.189656+0800 GCDDemo[2999:527321] 当前线程是:{number = 1, name = main} asyncGroupWaitTask---继续
2019-04-02 17:27:27.189704+0800 GCDDemo[2999:527373] 第一并发 -0- 当前线程是:{number = 3, name = (null)}
2019-04-02 17:27:27.189815+0800 GCDDemo[2999:528259] 第二并发 -0- 当前线程是:{number = 9, name = (null)}
2019-04-02 17:27:27.189969+0800 GCDDemo[2999:527373] 第一并发 -1- 当前线程是:{number = 3, name = (null)}
2019-04-02 17:27:27.190108+0800 GCDDemo[2999:528259] 第二并发 -1- 当前线程是:{number = 9, name = (null)}
2019-04-02 17:27:27.190361+0800 GCDDemo[2999:527321] 当前线程是:{number = 1, name = main} asyncGroupWaitTask---结束
2019-04-02 17:27:28.193774+0800 GCDDemo[2999:527321] 第二并发 -0- 发回掉结束 当前线程是:{number = 11, name = (null)} 随机等待:1秒
2019-04-02 17:27:29.193748+0800 GCDDemo[2999:527321] 第二并发 -1- 发回掉结束 当前线程是:{number = 9, name = (null)} 随机等待:2秒
2019-04-02 17:27:29.193998+0800 GCDDemo[2999:527321] 第一并发 -0- 发回掉结束 当前线程是:{number = 10, name = (null)} 随机等待:2秒
2019-04-02 17:27:31.191027+0800 GCDDemo[2999:527321] 第一并发 -1- 发回掉结束 当前线程是:{number = 3, name = (null)} 随机等待:4秒


  • 从打印几个来看,dispatch_group_wait能保证我们的目标操作一定能在我们的前两个并发的耗时操作之后执行dispatch_group_wait执行之后执行后的内容。
  • 暂停当前线程(阻塞当前线程),等待指定的group 中的任务执行完成后,才会往下继续执行
  • 无法控制回掉并发任务的回掉。

6.3、方案3:dispatch_group_enter+dispatch_group_leave


#pragma mark -
#pragma mark -  dispatch_group_enter dispatch_group_leave
- (void)asyncGroupEnterAndLeaveTask{
    
    NSLog(@"当前线程是:%@ asyncGroupWaitTask---开始",[NSThread currentThread]);  // 打印当前线程
    dispatch_group_t group =  dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    NSLog(@"当前线程是:%@ asyncGroupWaitTask---第一个网络请求",[NSThread currentThread]);  // 打印当前线程
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"第一并发  当前线程是:%@",[NSThread currentThread]);  // 打印当前线程
        [self netWorkingComletionHandler:^(NSString *status) {
            NSLog(@"第一并发  发回掉结束 %@",status);
            dispatch_group_leave(group);
        }];
    });
  
    NSLog(@"当前线程是:%@ asyncGroupWaitTask---第二个网络请求",[NSThread currentThread]);  // 打印当前线程
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"第二并发  当前线程是:%@",[NSThread currentThread]);  // 打印当前线程
        [self netWorkingComletionHandler:^(NSString *status) {
            NSLog(@"第二并发  发回掉结束 %@",status);
            dispatch_group_leave(group);
        }];
    });
    
    NSLog(@"当前线程是:%@ asyncGroupWaitTask---第三个网络请求",[NSThread currentThread]);  // 打印当前线程
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"第三并发  当前线程是:%@",[NSThread currentThread]);  // 打印当前线程
        [self netWorkingComletionHandler:^(NSString *status) {
            NSLog(@"第三并发  发回掉结束 %@",status);
            dispatch_group_leave(group);
        }];
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"网络回掉任务全部完成  当前线程是:%@",[NSThread currentThread]);  // 打印当前线程
    });
    NSLog(@"当前线程是:%@ asyncGroupWaitTask---结束",[NSThread currentThread]);  // 打印当前线程
}

打印结果

2019-04-02 17:29:55.232737+0800 GCDDemo[2999:527321] GroupEnterAndLeave
2019-04-02 17:29:55.233021+0800 GCDDemo[2999:527321] 当前线程是:{number = 1, name = main} asyncGroupWaitTask---开始
2019-04-02 17:29:55.233251+0800 GCDDemo[2999:527321] 当前线程是:{number = 1, name = main} asyncGroupWaitTask---第一个网络请求
2019-04-02 17:29:55.233462+0800 GCDDemo[2999:527321] 当前线程是:{number = 1, name = main} asyncGroupWaitTask---第二个网络请求
2019-04-02 17:29:55.233508+0800 GCDDemo[2999:527373] 第一并发  当前线程是:{number = 3, name = (null)}
2019-04-02 17:29:55.233677+0800 GCDDemo[2999:527321] 当前线程是:{number = 1, name = main} asyncGroupWaitTask---第三个网络请求
2019-04-02 17:29:55.233687+0800 GCDDemo[2999:527373] 第二并发  当前线程是:{number = 3, name = (null)}
2019-04-02 17:29:55.233853+0800 GCDDemo[2999:527321] 当前线程是:{number = 1, name = main} asyncGroupWaitTask---结束
2019-04-02 17:29:55.233865+0800 GCDDemo[2999:527373] 第三并发  当前线程是:{number = 3, name = (null)}
2019-04-02 17:29:56.238667+0800 GCDDemo[2999:527321] 第一并发  发回掉结束 当前线程是:{number = 12, name = (null)} 随机等待:1秒
2019-04-02 17:29:56.238904+0800 GCDDemo[2999:527321] 第二并发  发回掉结束 当前线程是:{number = 13, name = (null)} 随机等待:1秒
2019-04-02 17:30:00.238412+0800 GCDDemo[2999:527321] 第三并发  发回掉结束 当前线程是:{number = 3, name = (null)} 随机等待:5秒
2019-04-02 17:30:00.238717+0800 GCDDemo[2999:527321] 网络回掉任务全部完成  当前线程是:{number = 1, name = main}

  • 完美切合目前的网络请求
  • 保证在每个网络请求的延时操作得到回掉之后在执行最后的操作。
  • dispatch_group_enter+dispatch_group_leave 必须是成对出现,比如必须在创建并发任务前,必须先执行 dispatch_group_enter(group);方法,再创建并发任务,在接收到任务的block回掉方法中再执行 dispatch_group_leave(group);

代码的下载地址demo;

你可能感兴趣的:(iOS 多线程 GCD(一))