GCD实现同步方法

在iOS多线程中我们知道NSOperationQueue操作队列可以直接使用addDependency函数设置操作之间的依赖关系实现线程同步,还可以使用setMaxConcurrentOperationCount函数直接设置最大并发数量。那么在GCD中又是如何实现线程同步和控制最大并发数量的呢?

事实上在之前的问题中我们已经提到了GCD实现线程同步的两种方法了,一种是组队列(dispatch_group_t),另一种是dispatch_barrier_(a)sync,都是等待前面的任务完成后再执行某个任务。除此之外另外一种实现线程同步的方法是信号量机制。

GCD实现线程同步的方法:

组队列(dispatch_group):

举一个例子:用户下载一个图片,图片很大,需要分成很多份进行下载,使用GCD应该如何实现?使用什么队列?

使用Dispatch Group追加block到Global Group Queue,这些block如果全部执行完毕,就会执行通过dispatch_group_notify添加到主队列中的block,进行图片的合并处理。

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):

通过dispatch_barrier_async添加的操作会暂时阻塞当前队列,即等待前面的并发操作都完成后执行该阻塞操作,待其完成后后面的并发操作才可继续。可以将其比喻为一根霸道的独木桥,是并发队列中的一个并发障碍点,或者说中间瓶颈,临时阻塞并独占。注意dispatch_barrier_async只有在并发队列中才能起作用,在串行队列中队列本身就是独木桥,将失去其意义。

可见使用dispatch_barrier_async可以实现类似dispatch_group_t组调度的效果,同时主要的作用是避免数据竞争,高效访问数据。

/* 创建并发队列 */
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");
});
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

信号量机制(dispatch_semaphore):

信号量机制主要是通过设置有限的资源数量来控制线程的最大并发数量以及阻塞线程实现线程同步等。

GCD中使用信号量需要用到三个函数:

  • dispatch_semaphore_create用来创建一个semaphore信号量并设置初始信号量的值;
  • dispatch_semaphore_signal发送一个信号让信号量增加1(对应PV操作的V操作);
  • dispatch_semaphore_wait等待信号使信号量减1(对应PV操作的P操作);

那么如何通过信号量来实现线程同步呢?下面介绍使用GCD信号量来实现任务间的依赖和最大并发任务数量的控制。

使用信号量实现任务2依赖于任务1,即任务2要等待任务1结束才开始执行:

方法很简单,创建信号量并初始化为0,让任务2执行前等待信号,实现对任务2的阻塞。然后在任务1完成后再发送信号,从而任务2获得信号开始执行。需要注意的是这里任务1和2都是异步提交的,如果没有信号量的阻塞,任务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];

通过打印的时间可以看到任务2是在任务1结束后紧接着执行的:

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开始

... ...
时间: 2024-08-05 10:35:59

GCD实现同步方法的相关文章

iOS开发之多线程技术——GCD篇

本篇将从四个方面对iOS开发中GCD的使用进行详尽的讲解: 一.什么是GCD 二.我们为什么要用GCD技术 三.在实际开发中如何使用GCD更好的实现我们的需求 一.Synchronous & Asynchronous 同步 & 异步 二.Serial Queues & Concurrent Queues 串行 & 并发 三.Global Queues全局队列 四.Main Queue主队列 五.同步的作用 六.dispatch_time延迟操作 七.线程安全(单例dispa

多线程开发----GCD

多线程之-GCD Grand Centeral Dispatch(宏大的中枢调度器) GCD中有2个核心概念 任务:执行什么操作 队列:用来存放任务 遵循FIFO(先进先出)原则 执行任务 同步方法: dispatch_sync 异步方法: dispatch_async 同步和异步的区别 同步:只能在当前线程中执行任务,不具备开启新线程的能力 异步:可以在新的线程中执行任务,具备开启新线程的能力 队列 并发队列 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务) 并发功能只有在异步(

GCD下的几种实现同步的方式

GCD多线程下,实现线程同步的方式有如下几种: 1.串行队列 2.并行队列 3.分组 4.信号量 实例: 去网上获取一张图片并展示在视图上. 实现这个需求,可以拆分成两个任务,一个是去网上获取图片,一个是展示在视图上. 这两个任务是有关联的,所以需要同步处理. 下面看这几种方式如何实现. 一. 1.串行队列 1.1[GCD相关:] (1)GCD下的dispatch_queue队列都是FIFO队列,都会按照提交到队列的顺序执行. 只是根据队列的性质,分为<1>串行队列:用户队列.主线程队列 &l

深入了解GCD

首先提出一些问题: dispatch_async 函数如何实现,分发到主队列和全局队列有什么区别,一定会新建线程执行任务么? dispatch_sync 函数如何实现,为什么说 GCD 死锁是队列导致的而不是线程,死锁不是操作系统的概念么? 信号量是如何实现的,有哪些使用场景? dispatch_group 的等待与通知.dispatch_once 如何实现? dispatch_source 用来做定时器如何实现,有什么优点和用途? dispatch_suspend 和 dispatch_res

ios 多线程小结----- GCD篇

//3 GCD(充分利用设备的多盒)-------------屏蔽了线程,只能看见任务 队列步骤两步,定制任务,将任务添加到队列.GCD将添加的任务,放到线程中去执行,自动执行,自动释放原则:先进先出,先添加的任务先执行,有别于栈的先进后出,先添加的任务后执行 -------GCD--- GCD有两个用来执行任务的函数:同步函数,异步函数同步:按顺序执行任务,同一条线程执行(不开线程)异步:同时执行任务(开启新线程) 同步,异步,并发,串行同步:当前线程执行,不具备开启新线程的能力异步:在新线程

多线程技术GCD

GCD是基于C语言的框架 工作原理: 让程序平行排队的特定任务,根据可用的处理资源,安排它们在任何可用的处理器上执行任务 要执行的任务可以是一个函数或者一个block 底层是通过线程实现的,不过程序员可以不必关注实现的细节 GCD中的FIFO队列称为dispatch queue,可以保证先进来的任务先得到执行 dispatch_notify 可以实现监听一组任务是否完成,完成后得到通知 GCD队列: 全局队列:所有添加到主队列中的任务都是并发执行的 串行队列:所有添加到串行队列中的任务都是顺序执

GCD的一些用法

GCD是Grand Central Dispatch 的缩写. 即多线程优化技术. 它可以提供线程安全的队列,串行队列和并行队列,同步和异步执行任务.在队列中, 有很多回调块的执行单位, 完成一个任务后就回调块继续执行.GCD负责线程的创建,管理及释放,我们不用管理者一块. GCD队列特点: 1. 不是为了通常的数据存储而设计的 2. 它没有取消功能, 没有随机访问功能 3. 使用合理的数据结构来解决问题 4. 自动的线程创建和回收 5. 通过块来实现回调, 极大简化代码复杂度 GCD队列类型

iOS多线程 NSThread/GCD/NSOperationQueue

http://www.cnblogs.com/kenshincui/p/3983982.html iOS开发系列--并行开发其实很容易 2014-09-20 23:34 by KenshinCui, 9738 阅读, 19 评论, 收藏,  编辑 --多线程开发 概览 大家都知道,在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算.可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行.但是机器码是按顺序执行的,一个复杂的多步操作只能一步步按顺序逐个执行.改变这种

小伙,多线程(GCD)看我就够了,骗你没好处!

多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能.具有这种能力的系统包括对称多处理机.多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器.再一个程序中,这些独立运行的程序片段叫做线程(Thread).利用它编程的概念就叫做多线程.具有多线程能力的计算机因有硬