举一个例子:用户下载一个图片,图片很大,需要分成很多份进行下载,使用 GCD 应该如何实现?使用什么队列?
使用 Dispatch Group 追加 block 到 Global Group Queue,这些 block 如果全部执行完毕,就会执行通过
添加到主队列中的 block,进行图片的合并处理。
- dispatch_group_notify
阻塞任务(dispatch_barrier):
- dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- dispatch_group_t group = dispatch_group_create();
- dispatch_group_async(group, queue, ^{ /*加载图片1 */ });
- dispatch_group_async(group, queue, ^{ /*加载图片2 */ });
- dispatch_group_async(group, queue, ^{ /*加载图片3 */ });
- dispatch_group_notify(group, dispatch_get_main_queue(), ^{
- // 合并图片… …
- });
通过
添加的操作会暂时阻塞当前队列,即等待前面的并发操作都完成后执行该阻塞操作,待其完成后后面的并发操作才可继续。可以将其比喻为一根霸道的独木桥,是并发队列中的一个并发障碍点,或者说中间瓶颈,临时阻塞并独占。注意 dispatch_barrier_async 只有在并发队列中才能起作用,在串行队列中队列本身就是独木桥,将失去其意义。
- dispatch_barrier_async
可见使用
可以实现类似 dispatch_group_t 组调度的效果, 同时主要的作用是避免数据竞争,高效访问数据。
- dispatch_barrier_async
- /* 创建并发队列 */
- dispatch_queue_t concurrentQueue = dispatch_queue_create("test.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
- /* 添加两个并发操作A和B,即A和B会并发执行 */
- dispatch_async(concurrentQueue, ^() {
- NSLog(@"OperationA");
- });
- dispatch_async(concurrentQueue, ^() {
- NSLog(@"OperationB");
- });
- /* 添加barrier障碍操作,会等待前面的并发操作结束,并暂时阻塞后面的并发操作直到其完成 */
- dispatch_barrier_async(concurrentQueue, ^() {
- NSLog(@"OperationBarrier!");
- });
- /* 继续添加并发操作C和D,要等待barrier障碍操作结束才能开始 */
- dispatch_async(concurrentQueue, ^() {
- NSLog(@"OperationC");
- });
- dispatch_async(concurrentQueue, ^() {
- NSLog(@"OperationD");
- });
信号量机制(dispatch_semaphore):
- 2017-04-04 12:25:02.344 SingleView[12818:3694480] OperationB
- 2017-04-04 12:25:02.344 SingleView[12818:3694482] OperationA
- 2017-04-04 12:25:02.345 SingleView[12818:3694482] OperationBarrier!
- 2017-04-04 12:25:02.345 SingleView[12818:3694482] OperationD
- 2017-04-04 12:25:02.345 SingleView[12818:3694480] OperationC
信号量机制主要是通过设置有限的资源数量来控制线程的最大并发数量以及阻塞线程实现线程同步等。
GCD 中使用信号量需要用到三个函数:
那么如何通过信号量来实现线程同步呢?下面介绍使用 GCD 信号量来实现任务间的依赖和最大并发任务数量的控制。
方法很简单,创建信号量并初始化为 0,让任务 2 执行前等待信号,实现对任务 2 的阻塞。然后在任务 1 完成后再发送信号,从而任务 2 获得信号开始执行。需要注意的是这里任务 1 和 2 都是异步提交的,如果没有信号量的阻塞,任务 2 是不会等待任务 1 的, 实际上这里使用信号量实现了两个任务的同步。
通过打印的时间可以看到任务 2 是在任务 1 结束后紧接着执行的:
- /* 创建一个信号量 */
- dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
- /* 任务1 */
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- /* 耗时任务1 */
- NSLog(@"任务1开始");
- [NSThread sleepForTimeInterval:3];
- NSLog(@"任务1结束");
- /* 任务1结束,发送信号告诉任务2可以开始了 */
- dispatch_semaphore_signal(semaphore);
- });
- /* 任务2 */
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- /* 等待任务1结束获得信号量, 无限等待 */
- dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
- /* 如果获得信号量则开始任务2 */
- NSLog(@"任务2开始");
- [NSThread sleepForTimeInterval:3];
- NSLog(@"任务2结束");
- });
- [NSThread sleepForTimeInterval:10];
通过信号量控制最大并发数量:
- 2017-06-02 21:21:37.777156+0800 OC[6869:324518] 任务1开始
- 2017-06-02 21:21:40.782648+0800 OC[6869:324518] 任务1结束
- 2017-06-02 21:21:40.782829+0800 OC[6869:324519] 任务2开始
- 2017-06-02 21:21:43.788198+0800 OC[6869:324519] 任务2结束
通过信号量控制最大并发数量的方法为:创建信号量并初始化信号量为想要控制的最大并发数量,例如想要保证最大并发数为 5,则信号量初始化为 5。然后在每个新任务执行前进行 P 操作,等待信号使信号量减 1;每个任务结束后进行 V 操作,发送信号使信号量加 1。这样即可保证信号量始终在 5 以内,当前最多也只有 5 个以内的任务在并发执行。
- /* 创建一个信号量并初始化为5 */
- dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
- /* 模拟1000个等待执行的任务,通过信号量控制最大并发任务数量为5 */
- for (int i = 0; i < 1000; i++) {
- /* 任务i */
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- /* 耗时任务1,执行前等待信号使信号量减1 */
- dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
- NSLog(@"任务%d开始", i);
- [NSThread sleepForTimeInterval:10];
- NSLog(@"任务%d结束", i);
- /* 任务i结束,发送信号释放一个资源 */
- dispatch_semaphore_signal(semaphore);
- });
- }
- [NSThread sleepForTimeInterval:1000];
- 打印结果为每次开启五个并发任务
- 2017-06-02 21:45:27.409067+0800 OC[7234:336757] 任务1开始
- 2017-06-02 21:45:27.409069+0800 OC[7234:336758] 任务2开始
- 2017-06-02 21:45:27.409103+0800 OC[7234:336759] 任务3开始
- 2017-06-02 21:45:27.409268+0800 OC[7234:336761] 任务4开始
- 2017-06-02 21:45:27.409887+0800 OC[7234:336756] 任务0开始
- 2017-06-02 21:45:37.415217+0800 OC[7234:336757] 任务1结束
- 2017-06-02 21:45:37.415370+0800 OC[7234:336759] 任务3结束
- 2017-06-02 21:45:37.415217+0800 OC[7234:336761] 任务4结束
- 2017-06-02 21:45:37.415217+0800 OC[7234:336758] 任务2结束
- 2017-06-02 21:45:37.415442+0800 OC[7234:336756] 任务0结束
- 2017-06-02 21:45:37.415544+0800 OC[7234:336760] 任务5开始
- 2017-06-02 21:45:37.415548+0800 OC[7234:336762] 任务6开始
- 2017-06-02 21:45:37.415614+0800 OC[7234:336765] 任务9开始
- 2017-06-02 21:45:37.415620+0800 OC[7234:336764] 任务8开始
- 2017-06-02 21:45:37.415594+0800 OC[7234:336763] 任务7开始
- ... ...
来源: http://www.bubuko.com/infodetail-2436411.html