iOS中GCD详解

1,807 阅读17分钟

分享是每个优秀的程序员所必备的品质


简介

GCD

  • 全称是Grand Central Dispatch :伟大的中枢调度器
  • 纯C语言,提供了非常多强大的函数

优势

  • GCD是苹果公司为多核的并行运算提出的解决方案
  • GCD会自动利用更多的CPU内核(比如双核、四核)
  • GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
  • 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

劣势

  • 开启线程需要占用一定内存空间(默认情况下,每一条线程占用512KB)
  • 多线程,CPU调度的开销越大
  • 线程越多,程序设计越复杂,线程间通信、线程安全等问题突出。

顺便复习下线程的相关概念。

核心概念

  • 进程:指在系统中正在运行的一个应用程序。进程之间是独立的,每个进程运行在其专用的且受保护的内存中(如一个个独立的App和客户端)。

  • 线程:线程是进程的基本单位。一个进程的所有任务都在线程中执行,进程要想执行任务,必须得有一个或多个线程。程序启动时,默认会开启一条线程,这条线程称之为主线程或者UI线程。

  • 队列:用来存放任务,是任务的集合。按先进先出(FIFO)管理任务对象的数据结构;通常我们将队列分为4种:串行队列、并发队列、主队列(特殊的串行队列)、全局队列(特殊的并发队列)。

  • 同步(sync):按顺序执行,必须是前任务一个执行完,才能执行下一个。没有开启新线程的能力。

  • 异步(async):任务在同一时间可以一起执行。异步有开启新线程的能力,但是不一定会开启新线程。如在主队列中就不会开启新线程。严格来说,异步才有多线程的概念。

  • 任务:执行的操作

队列

使用dispatch_queue_create函数创建队列

// 参数1:队列名称,注意传c字符 如:"RC"
// 参数2:队列的类型  DISPATCH_QUEUE_CONCURRENT:并发 ,SERIAL:串行
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);

队列有四个优先级

     #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 // 后台

补充:全局队列在整个应用程序中是默认存在的,并且对应有高、默认、低和后台优先级一共四个并发队列,使用的时候只是选择其中一个直接拿来使用。而使用create是手动创建一个新的队列。

串行队列(Serial Dispatch Queue)

  • 让任务一个接着一个地按顺序依次执行(一个任务执行完毕后,再执行下一个任务),符合先进先出的基本原则,队列后面的任务必须等待前面的任务执行完毕后才出队列。但是,不要认为串行队列中的所有任务都在同一个线程中执行,串行队列中的异步任务,可能会开启新线程去执行。

GCD中获得串行有2种途径

1、使用dispatch_queue_create函数创建串行队列

// 创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)
dispatch_queue_t queue = dispatch_queue_create("RC", DISPATCH_QUEUE_SERIAL); 

2、使用主队列(下面会讲到)

并发队列(Concurrent Dispatch Queue)

  • 并发功能只有在异步(dispatch_async)函数下才有效
  • 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务),只要有可用线程,则队列头部任务将持续出队列。
dispatch_queue_create("queue.name", DISPATCH_QUEUE_CONCURRENT);

主队列(Main Dispatch Queue)

  • 主队列是GCD自带的一种特殊的串行队列
  • 放在主队列中的任务,都会放到主线程中执行,无法开辟新的线程。
  • 主队列下的任务不管是异步任务还是同步任务都不会开辟线程,任务只会在主线程顺序执行。

使用dispatch_get_main_queue()获得主队列

dispatch_get_main_queue()

全局队列(Global Dispatch Queue)

  • 本质是一个特殊的并发队列
  • GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建

使用dispatch_get_global_queue函数获得全局的并发队列

// 参数1: 队列的优先级 dispatch_queue_priority_t
// 参数2: 服务质量 unsigned long flags ,此参数暂时无用,用0即可
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

GCD的使用

  • 1、定制任务,确定想做的事情
  • 2、将任务添加到队列中,GCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则:先进先出,后进后出

1、串行队列 + 同步函数 :顺序执行,先进先出,不开启新线程

-(void)syncSerial{
    dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
    NSLog(@"---start---");
    dispatch_sync(queue, ^{
        NSLog(@"1----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3----%@",[NSThread currentThread]);
    });
    NSLog(@"---end----");
}

打印:

RCGCDDemo[3909:189316] ---start---
RCGCDDemo[3909:189316] 1----<NSThread: 0x600003644fc0>{number = 1, name = main}
RCGCDDemo[3909:189316] 2----<NSThread: 0x600003644fc0>{number = 1, name = main}
RCGCDDemo[3909:189316] 3----<NSThread: 0x600003644fc0>{number = 1, name = main}
RCGCDDemo[3909:189316] ---end----

2、串行队列 + 异步函数 :顺序执行,先进先出,可能会开启新线程

- (void)asyncSerial{
    //1.创建队列
    dispatch_queue_t queue = dispatch_queue_create("RC",  DISPATCH_QUEUE_SERIAL);
    NSLog(@"---satrt----");
    dispatch_async(queue, ^{
        NSLog(@"1----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3----%@",[NSThread currentThread]);
    });
    NSLog(@"---end----");
}

打印:

RCGCDDemo[3765:181439] ---satrt----
RCGCDDemo[3765:181439] ---end----
RCGCDDemo[3765:181473] 1----<NSThread: 0x600001013740>{number = 3, name = (null)}
RCGCDDemo[3765:181473] 2----<NSThread: 0x600001013740>{number = 3, name = (null)}
RCGCDDemo[3765:181473] 3----<NSThread: 0x600001013740>{number = 3, name = (null)}

3、并发队列 + 同步函数 :顺序执行,先进先出,不会开启新线程

-(void)syncConcurrent{
    dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"---start---");
    dispatch_sync(queue, ^{
        NSLog(@"1----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3----%@",[NSThread currentThread]);
    });
    NSLog(@"---end----");
}

打印:

RCGCDDemo[3825:184734] ---start---
RCGCDDemo[3825:184734] 1----<NSThread: 0x600000c19400>{number = 1, name = main}
RCGCDDemo[3825:184734] 2----<NSThread: 0x600000c19400>{number = 1, name = main}
RCGCDDemo[3825:184734] 3----<NSThread: 0x600000c19400>{number = 1, name = main}
RCGCDDemo[3825:184734] ---end----

4、并发队列 + 异步函数 :同时执行,开启多条线程,真正的多线程

- (void)asyncConcurrent{
    // 1.创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    NSLog(@"---satrt----");
    // 2.添加任务
    dispatch_async(queue, ^{
        NSLog(@"1----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3----%@",[NSThread currentThread]);
    });
    NSLog(@"---end----");
}

打印:

RCGCDDemo[3650:175994] ---satrt----
RCGCDDemo[3650:175994] ---end----
RCGCDDemo[3650:176035] 2----<NSThread: 0x600002895d40>{number = 3, name = (null)}
RCGCDDemo[3650:176114] 1----<NSThread: 0x600002895c80>{number = 4, name = (null)}
RCGCDDemo[3650:176115] 3----<NSThread: 0x60000289ae80>{number = 5, name = (null)}

注意:并不是一个任务对应开启一条线程,具体需要开启多少条线程由系统决定

5、主队列 + 同步函数 :顺序执行,先进先出,不会开启新线程 (注意死锁发生)

注意:该方法在主线程中执行发生死锁;该方法在子线程中执行,那么所有的任务在主线程中执行。

-(void)syncMain{
    dispatch_queue_t queue = dispatch_get_main_queue();
    NSLog(@"start----");
    dispatch_sync(queue, ^{
        NSLog(@"1----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3----%@",[NSThread currentThread]);
    });
    NSLog(@"end---");
}

在主线程中执行-(void)syncMain方法,会因为相互等待而发生死锁,程序崩溃! 如果在主线程执行该方法,该任务1、2、3因为加入了主队列所以也会在主线程中执行。当执行到任务1的时候,因为当前主线程正在等待任务1、2、3执行结束,才会再执行其他任务,这就造成了任务1等待-(void)syncMain方法执行结束,而-(void)syncMain需要方法里面的所以任务执行完毕才会结束(需要任务1、2、3执行完毕),-(void)syncMain和任务1相互等待造成死锁!!!

在子线程中执行的打印:

RCGCDDemo[4104:198438] start----
RCGCDDemo[4104:198392] 1----<NSThread: 0x600001f2c700>{number = 1, name = main}
RCGCDDemo[4104:198392] 2----<NSThread: 0x600001f2c700>{number = 1, name = main}
RCGCDDemo[4104:198392] 3----<NSThread: 0x600001f2c700>{number = 1, name = main}
RCGCDDemo[4104:198438] end---

6、主队列 + 异步函数:在主线程中顺序执行,先进先出,不会开启新线程

//异步函数+主队列
-(void)asyncMain{
    dispatch_queue_t queue = dispatch_get_main_queue();
    NSLog(@"---start---");
    dispatch_async(queue, ^{
        NSLog(@"1----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3----%@",[NSThread currentThread]);
    });
    NSLog(@"---end----");
}

打印:

RCGCDDemo[4050:195922] ---start---
RCGCDDemo[4050:195922] ---end----
RCGCDDemo[4050:195922] 1----<NSThread: 0x6000013ead80>{number = 1, name = main}
RCGCDDemo[4050:195922] 2----<NSThread: 0x6000013ead80>{number = 1, name = main}
RCGCDDemo[4050:195922] 3----<NSThread: 0x6000013ead80>{number = 1, name = main}

GCD线程间通信

以在子线程下载,主线程显示为例

- (void)laodImg{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 下载图片操作
        dispatch_sync(dispatch_get_main_queue(), ^{
            // 显示图片
        });
    });
}

GCD常用函数

信号量 Dispatch Semaphore

是持有计数的信号。 Dispatch Semaphore 提供了三个函数

  • dispatch_semaphore_create:创建一个 Semaphore 并初始化信号的总量
  • dispatch_semaphore_signal:发送一个信号,让信号总量加1
  • dispatch_semaphore_walt:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。

实际开发中主要用于:

  • 保持线程同步,通过设置最大开辟的线程数为1,将异步执行任务转换为同步执行任务
  • 保证线程安全,为线程加锁
保持线程同步

代码:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block NSInteger number = 0;
NSLog(@"semaphore --- start,number = %zd",number);
dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_async(global, ^{
    number = 1;
    dispatch_semaphore_signal(semaphore);
    NSLog(@"semaphore --- signal");
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore --- end,number = %zd",number); 

这里的全局队列使用的是DISPATCH_QUEUE_PRIORITY_LOW,低等级。是为了防止高优先级的工作依赖于较低优先级的工作时造成优先级翻转。下同

输出:

semaphore --- start,number = 0
semaphore --- signal
semaphore --- start,number = 1

正常情况下,异步任务在全局队列中,不会阻塞后面的任务,但是dispatch_semaphore_wait 加锁阻塞了当前线程,dispatch_semaphore_signal解锁后当前线程继续执行。

设置最大开辟的线程数

假设我们要下载10张图片,并发异步进行,每个下载都会开辟一个新线程,可是我们担心线程太多了CPU开销过大怎么办?这里就需要用信号量控制一下最大开辟线程数。

代码:

// 设置最大开辟的线程数为3
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
// 创建一个并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
// 开启的是10个异步的操作,通过信号量,让10个异步的最多并发量为3个,剩下的同步等待
for (NSInteger i = 0; i < 10; i++) {
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"执行任务 %ld", i);
        sleep(1);
        NSLog(@"完成当前任务 %ld", i);
        dispatch_semaphore_signal(semaphore);
    });
}

输出:

执行任务 0
执行任务 1
执行任务 2
完成当前任务 0
完成当前任务 2
完成当前任务 1
执行任务 4
执行任务 3
执行任务 5
完成当前任务 3
执行任务 6
完成当前任务 5
执行任务 7
完成当前任务 4
执行任务 8
完成当前任务 6
完成当前任务 7
执行任务 9
完成当前任务 8
完成当前任务 9

执行结果表明:最多同时执行3个异步操作,剩下的同步等待,只有前面的执行结束。才可以进行剩下的异步操作。

保持线程安全,加锁

在线程安全中可以将 dispatch_semaphore_wait 看作加锁,而 dispatch_semaphore_signal 看作解锁。

首先创建全局变量: _semaphore = dispatch_semaphore_create(1); 注意到这里的初始化信号量是1,方便起始起始调用dispatch_semaphore_wait信号量-1,后续可以正常执行。

代码:

_semaphore = dispatch_semaphore_create(1);
for(NSInteger i=0;i<10;i++){
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
         [self asyncTask];
     });
}
- (void)asyncTask{
    dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
    _count += 1;
    sleep(1);
    NSLog(@"执行任务:%zd",_count);
    dispatch_semaphore_signal(_semaphore);
}

输出:任务1-任务10,顺序执行。

原因:在子线程中并发执行 asyncTask,那么第一个添加到并发队列里的,会将信号量减1,此时信号量等于0, 可以执行接下来的任务。而并发队列中其他任务,由于此时信号量不等于0,必须等当前正在执行的任务 执行完毕后调用dispatch_semaphore_signal将信号量加1,才可以继续执行接下来的任务,以此类推,从而达到线程加锁的目的。

延迟函数

- (void)afterDelay{
    //1. 延迟执行的第一种方法
    //[self performSelector:@selector(task) withObject:nil afterDelay:1.0];
    
    //2.延迟执行的第二种方法
    //[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(task) userInfo:nil repeats:YES];
   
    //3.GCD
    /* 参数1: DISPATCH_TIME_NOW 从现在开始计算时间
     * 参数2: 延迟的时间 1.0 GCD时间单位:纳秒
     * 参数3: 队列
     */
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), queue, ^{
        // 
    });
}
  • dispatch_after 能让我们添加进队列的任务延时执行,该函数并不是在指定时间后执行处理,而只是在指定时间追加处理到dispatch_queue
  • 由于其内部使用的是 dispatch_time_t 管理时间,而不是 NSTimer。所以可以在子线程中调用,相比 performSelector:afterDelay,不用关心Runloop 是否开启

栅栏函数(控制任务的执行顺序)

有两个函数:dispatch_barrier_asyncdispatch_barrier_sync

注意:官方说明在使用栅栏函数时,使用自定义队列才有意义,如果用的是串行队列或者系统提供的全局并发队列,这个栅栏函数的作用等同于一个同步函数

  • dispatch_barrier_async

代码:

- (void)barrierAsync{
    // 必须是自定义的并发队列
    dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"---satrt----");
    dispatch_async(queue, ^{
        NSLog(@"1----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2----%@",[NSThread currentThread]);
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"3----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"4----%@",[NSThread currentThread]);
    });
    NSLog(@"---end----");
}

打印:

RCGCDDemo[4559:227700] ---satrt----
RCGCDDemo[4559:227700] ---end----
RCGCDDemo[4559:230128] 2----<NSThread: 0x600002f5f0c0>{number = 8, name = (null)}
RCGCDDemo[4559:228512] 1----<NSThread: 0x600002f5e840>{number = 5, name = (null)}
RCGCDDemo[4559:228512] 3----<NSThread: 0x600002f5e840>{number = 5, name = (null)}
RCGCDDemo[4559:228512] 4----<NSThread: 0x600002f5e840>{number = 5, name = (null)}

可得:

  • barrier前面的任务都是并发执行的
  • 执行到barrier时,不用执行完barrier,就可以将后面的任务插入到队列中,但是仍然需要执行完barrier,才会执行后面的任务。
  • dispatch_barrier_sync
- (void)barrierSync{
    // 必须是自定义的并发队列
    dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"---satrt----");
    dispatch_async(queue, ^{
        NSLog(@"1----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2----%@",[NSThread currentThread]);
    });
    dispatch_barrier_sync(queue, ^{
        NSLog(@"3----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"4----%@",[NSThread currentThread]);
    });
    NSLog(@"---end----");
}

打印:

RCGCDDemo[4653:234416] ---satrt----
RCGCDDemo[4653:234775] 1----<NSThread: 0x600001783580>{number = 5, name = (null)}
RCGCDDemo[4653:235167] 2----<NSThread: 0x60000178e6c0>{number = 8, name = (null)}
RCGCDDemo[4653:234416] 3----<NSThread: 0x6000017d1400>{number = 1, name = main}
RCGCDDemo[4653:234416] ---end----
RCGCDDemo[4653:235167] 4----<NSThread: 0x60000178e6c0>{number = 8, name = (null)}

可得:

  • 同样,barrier前面的任务都是并发执行的
  • 运行到barrier时,必须等到barrier执行结束后才会将后面的任务插入到队列中,继续执行。

一次性代码

注意:不能放到懒加载,主要用于单例类

快速迭代

开多个线程并发完成迭代操作

- (void)applyTest{
    // 开子线程和主线程一起完成遍历任务,任务的执行时并发的
    /*
     参数1: 遍历的次数
     参数2: 队列(并发队列)
     参数3: index 索引
     */
    dispatch_apply(5, dispatch_get_global_queue(0, 0), ^(size_t index) {
        //添加耗时操作,效果更明显
        for(NSInteger i=0;i<100000;i++){
        }
        NSLog(@"%zd---%@",index,[NSThread currentThread]);
    });
}

打印:

RCGCDDemo[4969:248588] 0---<NSThread: 0x600002fd5140>{number = 1, name = main}
RCGCDDemo[4969:248654] 2---<NSThread: 0x600002f872c0>{number = 3, name = (null)}
RCGCDDemo[4969:248652] 1---<NSThread: 0x600002f87080>{number = 4, name = (null)}
RCGCDDemo[4969:248655] 3---<NSThread: 0x600002f87280>{number = 5, name = (null)}
RCGCDDemo[4969:248588] 4---<NSThread: 0x600002fd5140>{number = 1, name = main}

队列组

作用与栅栏函数相似:当队列组中所有的任务都执行完毕的时候,会发出通知 dispatch_group_t group = dispatch_group_create(); 创建创建队列组, dispatch_group_notify 拦截队列组中所有任务完成的通知 代码:

- (void)groupTest{
    // 1.创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 2.创建队列组
    dispatch_group_t group = dispatch_group_create();
    NSLog(@"---satrt----");
    // 3. 添加任务,并监听任务的执行情况,通知group
    dispatch_group_async(group, queue, ^{
        NSLog(@"1----%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"2----%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"3----%@",[NSThread currentThread]);
    });
    //拦截通知,当队列组中所有的任务都执行完毕的时候回进入到下面的方法
    dispatch_group_notify(group, queue, ^{
        NSLog(@"-------come,baby");
    });
    NSLog(@"---end----");
}

打印:

RCGCDDemo[934:25071] ---satrt----
RCGCDDemo[934:25071] ---end----
RCGCDDemo[934:25126] 1----<NSThread: 0x600003ec8640>{number = 5, name = (null)}
RCGCDDemo[934:25125] 3----<NSThread: 0x600003ecc800>{number = 4, name = (null)}
RCGCDDemo[934:25124] 2----<NSThread: 0x600003ec8740>{number = 6, name = (null)}
RCGCDDemo[934:25124] -------come,baby

可知:使用dispatch_group_notify是可以监听队列组中的异步函数执行情况,并且本身也是异步执行的,不会阻塞线程。

还可以使用dispatch_group_enterdispatch_group_leave来达到相同的功能。dispatch_group_enter可以使得后面的异步任务会被纳入到队列组的监听范围,进入群组,两个方法必须要配对使用 代码:

- (void)groupTest2{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_t group = dispatch_group_create();
    NSLog(@"---satrt----");
    
    //后面的异步任务会被纳入到队列组的监听范围
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"1----%@",[NSThread currentThread]);
        //离开群组
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"2----%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"3----%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    
    //拦截通知,当队列组中所有的任务都执行完毕的时候回进入到下面的方法
    dispatch_group_notify(group, queue, ^{
        NSLog(@"-------come,baby");
    });
    NSLog(@"---end----");
}

打印:

RCGCDDemo[993:28534] ---satrt----
RCGCDDemo[993:28534] ---end----
RCGCDDemo[993:28621] 1----<NSThread: 0x6000002af600>{number = 3, name = (null)}
RCGCDDemo[993:28868] 3----<NSThread: 0x6000002afbc0>{number = 5, name = (null)}
RCGCDDemo[993:28867] 2----<NSThread: 0x6000002a2340>{number = 4, name = (null)}
RCGCDDemo[993:28867] -------come,baby

还可以使用dispatch_group_wait来监听队列组中任务的执行情况,本身是阻塞的,直到队列组中所有的任务执行结束后才会执行后面方法。

// 代码同上
// 阻塞的 直到队列组中所有的任务都执行完毕之后才能执行
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"---end----");

打印:

RCGCDDemo[1054:32183] ---satrt----
RCGCDDemo[1054:32242] 1----<NSThread: 0x600000f5f480>{number = 3, name = (null)}
RCGCDDemo[1054:32241] 3----<NSThread: 0x600000f5f780>{number = 6, name = (null)}
RCGCDDemo[1054:32240] 2----<NSThread: 0x600000f5f300>{number = 4, name = (null)}
RCGCDDemo[1054:32183] ---end----

可知:等任务1、2、3全部执行完毕后,才会打印后面的 “end”。


个人浅见,有误的地方欢迎指正