Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。下面介绍GCD的基础使用.
0x01 基本组合
两种队列+两种任务执行方式
异步(async)不等待,和非主队列的组合会默认开启线程。
1、同步+并发 = 串行
1 | //MARK: => 同步+并发=串行 |
打印结果:
1 | currentThread---<NSThread: 0x600003cca900>{number = 1, name = main} |
2、异步+并发=并行
1 | //MARK: => 异步+并发=并行 |
打印结果:
因为追加的三个任务为异步执行,所以可先执行end。
1 | currentThread---<NSThread: 0x600003959300>{number = 1, name = main} |
3、同步+串行=串行
1 | NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 |
打印结果:
1 | currentThread---<NSThread: 0x600002330000>{number = 1, name = main} |
4、异步+串行=串行
1 | NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 |
打印结果1
2
3
4
5
6
7
8
9currentThread---<NSThread: 0x600001538b00>{number = 1, name = main}
asyncSerial---begin
asyncSerial---end
1---<NSThread: 0x600001540240>{number = 3, name = (null)}
1---<NSThread: 0x600001540240>{number = 3, name = (null)}
2---<NSThread: 0x600001540240>{number = 3, name = (null)}
2---<NSThread: 0x600001540240>{number = 3, name = (null)}
3---<NSThread: 0x600001540240>{number = 3, name = (null)}
3---<NSThread: 0x600001540240>{number = 3, name = (null)}
5、主队列中同步执行任务。
因为默认下面代码是在主队列中执行的。简称下面代码为任务A。
当追加任务1后,因为是同步执行,任务1需要等到任务A执行完毕后再执行任务1。而任务A的执行依赖于任务1.所以会造成死锁。
1 | NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 |
6、主队列+异步
1 | NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 |
打印结果
效果同:串行队列+异步执行
1 | currentThread---<NSThread: 0x600002b65240>{number = 1, name = main} |
7、线程通信
1 | // 获取全局并发队列 |
打印结果
在全局队列中,异步添加主队列任务。
主队列任务不会阻塞全局队列的任务,但是不确定主队列任务在何时执行完。
1 | 1-0--<NSThread: 0x60000201bfc0>{number = 3, name = (null)} |
0x02 栅栏函数
dispatch_barrier_async函数的作用与barrier的意思相同,在进程管理中起到一个栅栏的作用,它等待所有位于barrier函数之前的操作执行完毕后执行。
值得注意的是:
1、在barrier函数执行之后,barrier函数之后的操作才会得到执行。
2、该函数需要同dispatch_queue_create函数生成的concurrent Dispatch Queue队列一起使用。全局队列在此使用是无效的。
1 | //创建并发队列。 |
打印结果
1 | 2---<NSThread: 0x6000030f5440>{number = 4, name = (null)} |
0x03 队列组
在使用GCD进行任务操作时,有时会希望若干个任务执行之间有先后执行的依赖关系,
例如,当A、B两个异步任务完成后,再去完成C任务,
这时就可以使用队列组dispatch group来完成。
1、组合一
dispatch_group_t
dispatch_group_async
dispatch_group_notify
1 | NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 |
打印结果
notify中的任务会在线程组的其它任务执行完成后执行。
1 | currentThread---<NSThread: 0x600000f62940>{number = 1, name = main} |
2、组合二
暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行。
1、dispatch_group_t
2、dispatch_group_async
3、dispatch_group_wait 阻塞。
1 | NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 |
打印结果
1 | currentThread---<NSThread: 0x600003d47f00>{number = 1, name = main} |
3、组合三
dispatch_group_enter、
dispatch_group_leave
dispatch_group_notify
1 | NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 |
打印结果
1 | currentThread---<NSThread: 0x600003b2c580>{number = 1, name = main} |
0x04 信号量
dispatch_semaphore_create:创建一个Semaphore并初始化信号的总量
dispatch_semaphore_signal:发送一个信号,让信号总量加1
dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。
保持线程同步,将异步执行任务转换为同步执行任务
保证线程安全,为线程加锁
信号量在等待的时候,耗能较少。
1 | dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); |
同步打印:
1 | i=0---thread==<NSThread: 0x600000e4a080>{number = 3, name = (null)} |
0x05 通过信号量保证线程安全
1 | - (void)gcdTest16{ |
1 | - (void)saleTicketNotSafe { |
0x06 快速迭代,并发遍历
1 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); |
打印结果
无序,但是保证每个都被遍历到。
1 | apply---begin |
0x07 其它用法
延迟执行
1 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ |
单例用法
1 | static dispatch_once_t onceToken; |
0x08 备注
更多GCD介绍参考iOS多线程:GCD详尽总结
本文Demo:DyGCDTest.m