在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, );
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");
});
-- ::02.344 SingleView[:] OperationB
-- ::02.344 SingleView[:] OperationA
-- ::02.345 SingleView[:] OperationBarrier!
-- ::02.345 SingleView[:] OperationD
-- ::02.345 SingleView[:] 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(); /* 任务1 */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
/* 耗时任务1 */
NSLog(@"任务1开始");
[NSThread sleepForTimeInterval:];
NSLog(@"任务1结束");
/* 任务1结束,发送信号告诉任务2可以开始了 */
dispatch_semaphore_signal(semaphore);
}); /* 任务2 */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
/* 等待任务1结束获得信号量, 无限等待 */
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
/* 如果获得信号量则开始任务2 */
NSLog(@"任务2开始");
[NSThread sleepForTimeInterval:];
NSLog(@"任务2结束");
});
[NSThread sleepForTimeInterval:];

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

-- ::37.777156+ OC[:] 任务1开始
-- ::40.782648+ OC[:] 任务1结束
-- ::40.782829+ OC[:] 任务2开始
-- ::43.788198+ OC[:] 任务2结束

通过信号量控制最大并发数量:

通过信号量控制最大并发数量的方法为:创建信号量并初始化信号量为想要控制的最大并发数量,例如想要保证最大并发数为5,则信号量初始化为5。然后在每个新任务执行前进行P操作,等待信号使信号量减1;每个任务结束后进行V操作,发送信号使信号量加1。这样即可保证信号量始终在5以内,当前最多也只有5个以内的任务在并发执行。

/* 创建一个信号量并初始化为5 */
dispatch_semaphore_t semaphore = dispatch_semaphore_create(); /* 模拟1000个等待执行的任务,通过信号量控制最大并发任务数量为5 */
for (int i = ; i < ; i++) {
/* 任务i */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
/* 耗时任务1,执行前等待信号使信号量减1 */
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"任务%d开始", i);
[NSThread sleepForTimeInterval:];
NSLog(@"任务%d结束", i);
/* 任务i结束,发送信号释放一个资源 */
dispatch_semaphore_signal(semaphore);
});
}
[NSThread sleepForTimeInterval:]; 打印结果为每次开启五个并发任务
-- ::27.409067+ OC[:] 任务1开始
-- ::27.409069+ OC[:] 任务2开始
-- ::27.409103+ OC[:] 任务3开始
-- ::27.409268+ OC[:] 任务4开始
-- ::27.409887+ OC[:] 任务0开始 -- ::37.415217+ OC[:] 任务1结束
-- ::37.415370+ OC[:] 任务3结束
-- ::37.415217+ OC[:] 任务4结束
-- ::37.415217+ OC[:] 任务2结束
-- ::37.415442+ OC[:] 任务0结束 -- ::37.415544+ OC[:] 任务5开始
-- ::37.415548+ OC[:] 任务6开始
-- ::37.415614+ OC[:] 任务9开始
-- ::37.415620+ OC[:] 任务8开始
-- ::37.415594+ OC[:] 任务7开始 ... ...

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

  1. ios高级开发之多线程(三)GCD技术

    GCD是基于C的API,它是libdispatch的的市场名称.而libdispatch作为Apple公司的一个库,为并发代码在多核硬件(跑IOS或者OS X)上执行提供有力支持. 那么我们为什么要用 ...

  2. 修改版: 小伙,多线程(GCD)看我就够了,骗你没好处!

    多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能.具有这种能力的系 ...

  3. GCD 扫盲篇

    GCD有四个概念:串行队列.并行队列.同步.异步四者. 如下简介: 这里不仅给出了不确定性,而且也给出了确定性.对于初学者而言,有时候因为那些不确定的东西所造成的疑问会像没有闸却在疾驰的汽车一样让人惊 ...

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

    本篇将从四个方面对iOS开发中GCD的使用进行详尽的讲解: 一.什么是GCD 二.我们为什么要用GCD技术 三.在实际开发中如何使用GCD更好的实现我们的需求 一.Synchronous & ...

  5. iOS多线程之GCD详解

    GCD(Grand Central Dispatch)是基于C语言开发的一套多线程开发机制.也是目前苹果官方推荐的多线程开发方法.iOS三种多线程开发中GCD是抽象层次最高的.当然用起来也是最简单的. ...

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

    //3 GCD(充分利用设备的多盒)-------------屏蔽了线程,只能看见任务 队列步骤两步,定制任务,将任务添加到队列.GCD将添加的任务,放到线程中去执行,自动执行,自动释放原则:先进先出 ...

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

    GCD多线程下,实现线程同步的方式有如下几种: 1.串行队列 2.并行队列 3.分组 4.信号量 实例: 去网上获取一张图片并展示在视图上. 实现这个需求,可以拆分成两个任务,一个是去网上获取图片,一 ...

  8. 多线程&NSObject&NSThread&NSOperation&GCD

    1.NSThread 每个NSThread对象对应一个线程,量级较轻(真正的多线程) 以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去关心线程的具体使用问题 2.NSOperation/NS ...

  9. 多线程 -- GCD

    GCD中有2个核心概念 任务:执行什么操作 队列:用来存放任务 执行任务 同步方法: dispatch_sync dispatch_sync(dispatch_queue_t queue, dispa ...

随机推荐

  1. 【PAT】B1002 写出这个数

    思路: 1.以字符串形式输入数据,计算结果 2.使用sprintf将结果数字转换为字符串(将数字每一位分开) 3.分别输出字符每一位 1,笨方法,用的ifelse来判断输出 #include<s ...

  2. 路由交换04-----STP

    路由交换-----STP STP协议(生成树协议)逻辑上断开环路,防止二层网络的广播风暴的产生. 为什么需要STP协议? 假设有这样一个拓扑: 现在PC1ping网关192.168.1.3,而这个网关 ...

  3. January 12th, 2018 Week 02nd Friday

    Nothing behind me, everything ahead of me, as is ever so on the road. 我的身后空空荡荡,整个世界都在前方,这就是在路上. That ...

  4. web笔试

    类型判断用到哪些方法? typeof和instanceof 值类型和引用类型的区别? 根据 JavaScript中的变量类型传递方式,又分为值类型和引用类型,在参数传递方式上,值类型是按值传递,引用类 ...

  5. jQuery EasyUI combobox多选及赋值

      1.多选属性multiple multiple:true多选 multiple:false单选 <input class="easyui-combobox" id=&qu ...

  6. (16)Python练习题

    练习题一: 练习题二:

  7. 离线安装Cloudera Manager 5和CDH5(最新版5.9.3) 完全教程(四)数据库安装(单节点)

    一.卸载CentOS自带的MySQL 1.1 查看之前是否安装过mysql [root@master mysql]# rpm -qa|grep -i mysql mysql-libs--.el6.x8 ...

  8. (1) 天猫精灵接入Home Assistant- 网站论坛

    https://bbs.hassbian.com/forum-38-1.html 1051196347 123456 https://bbs.hassbian.com/thread-4054-1-1. ...

  9. cryptopunks测试代码cryptopunksmarket-setinitial.js

    require('babel-polyfill'); //测试用例要在执行完了truffle compile和truffle migrate后才能使用truffle test来进行测试 //要注意ar ...

  10. vue将表格导出为excel

    vue将表格导出为excel 一:在项目中需要安装2个依赖项,如下命令: npm install --save file-saver xlsx 二:在vue文件中如下使用即可: <templat ...