- 并行+异步:就是真正的并发,新开有有多个线程处理任务,任务并发执行(不按顺序执行)
- 串行+异步:新开一个线程,任务一个接一个执行,上一个任务处理完毕,下一个任务才可以被执行
- 并行+同步:不新开线程,任务一个接一个执行
- 串行+同步:不新开线程,任务一个接一个执行
基本常用用法
1.最常用用法
1 | //创建异步线程并运行 |
dispatch_async
//异步线程函数
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
//获取一个全局队列,并设置队列的优先级,一般设置DEFAULT
优先级设置的种类:
#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
//后台运行
0
保留字段备用,一般设置为0
2.dispatch_queue_t
挂起和继续
我们可以暂停一个 queue 以阻止它执行 block 对象,使用 dispatch_suspend
函数挂起一个 dispatch queue;使用 dispatch_resume
函数继续 dispatch queue。调 用 dispatch_suspend
会增加 queue 的挂起计数,调用 dispatch_resume
则减少queue 的挂起计数。当挂起计数大于 0 时,queue 就保持挂起状态。因此你必须对应地调用 suspend 和 resume 函数。
挂起和继续是异步的,而且只在执行 block 之间生效。挂起一个 queue 不会导致正在执行的 block 停止。
1 | //创建一个`dispatch_queue_t` |
3.Dispatch Queue 和线程安全性
使用 Dispatch Queue 实现应用并发时,也需要注意线程安全性:
Dispatch queue 本身是线程安全的。换句话说,你可以在应用的任意线程中提交任务到 dispatch queue,不需要使用锁或其它同步机制。
不要在执行任务代码中调用 dispatch_sync 函数调度相同的 queue,这样做会死锁这个 queue。如果你需要 dispatch 到当前 queue,需要使用 dispatch_async 函数异步调度。
避免在提交到 dispatch queue 的任务中获得锁,虽然在任务中使用锁是安全的,但在请求锁时,如果锁不可用,可能会完全阻塞串行 queue。类似的,并发 queue 等待锁也可能阻止其它任务的执行。如果代码需要同步,就使用串行 dispatch queue。
虽然可以获得运行任务的底层线程的信息,最好不要这样做。
4.Dispatch Group
用于监控一组 Block 对象完成(你可以同步或异步地监控 block)。Group 提供了一个非常有用的同步机制,你的代码可以等待其它任务的完成。
使用 Dispatch Group 等待 queue 中的一组任务
Dispatch group 用来阻塞一个线程,直到一个或多个任务完成执行。有时候 你必须等待任务完成的结果,然后才能继续后面的处理。dispatch group 也可以替代线程 join。
基本的流程是设置一个组,dispatch 任务到 queue,然后等待结果。你需要使用 dispatch_group_async
函数,会关联任务到相关的组和 queue。使用 dispatch_group_wait
等待一组任务完成。
1 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); |
5. dispatch_group_notify
dispatch_group 执行完一组异步操作后可以通过 dispatch_group_notify
来通知主线程,反馈信息给用户。
代码示例:
1 | //真实的异步并发多个异步任务 |
因为向Concurrent Dispatch Queue 追加处理,多个线程并行执行,所以追加处理的执行顺序不定。执行顺序会发生变化,但是此执行结果的done一定是最后输出的。
无论向什么样的Dispatch Queue中追加处理,使用Dispatch Group都可以监视这些处理执行的结果。一旦检测到所有处理执行结束,就可以将结束的处理追加到Dispatch Queue中,这就是使用Dispatch Group的原因。
下面试一个使用Dispatch Group异步下载两张图片,然后合并成一张图片的medo(注意,我们总是应该在主线程中更新UI):
1 | import "ViewController.h" |
注意:dispatch_group_notify
对串行队列无意义
自定义串行队列
1 | dispatch_queue_t myCustomQueue = dispatch_queue_create("test.customQueueName.MyCustomQueue", DISPATCH_QUEUE_SERIAL); |
因为串行队列里面任务本来就是一个执行完毕再接着执行下一个,所以我们只需在最后一个异步任务处理之前所执行的任务结果就可以了,据说串行队列没有并发队列效率高。
6. dispatch_barrier
资源锁
在队列中,barrier块必须单独执行,不能与其他block并行。这只对并发队列有意义,并发队列如果发现接下来要执行的block是个barrier block,那么就一直要等到当前所有并发的block都执行完毕,才会单独执行这个barrier block代码块,等到这个barrier block执行完毕,再继续正常处理其他并发block。在上面的代码中,setter方法中使用了barrier block以后,对象的读取操作依然是可以并发执行的,但是写入操作就必须单独执行了。
代码示例
1 | #import <Foundation/Foundation.h> |
7.使用 Dispatch Semaphore 控制有限资源的使用
1 | // 创建信号,指定初始池大小 |
8.Dispatch Source
Dispatch Source 在特定类型的系统事件发生时,会产生通知。你可以使用 Dispatch Source 来监控各种事件,如:进程通知、信号、描述符事件、等等。当事件发生时,Dispatch Source 异步地提交你的任务到指定的 Dispatch Queue 来进行处理。
看了博客但是还没有想得到具体使用的业务场景,待补充