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, );
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实现同步方法的更多相关文章
- ios高级开发之多线程(三)GCD技术
GCD是基于C的API,它是libdispatch的的市场名称.而libdispatch作为Apple公司的一个库,为并发代码在多核硬件(跑IOS或者OS X)上执行提供有力支持. 那么我们为什么要用 ...
- 修改版: 小伙,多线程(GCD)看我就够了,骗你没好处!
多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能.具有这种能力的系 ...
- GCD 扫盲篇
GCD有四个概念:串行队列.并行队列.同步.异步四者. 如下简介: 这里不仅给出了不确定性,而且也给出了确定性.对于初学者而言,有时候因为那些不确定的东西所造成的疑问会像没有闸却在疾驰的汽车一样让人惊 ...
- iOS开发之多线程技术——GCD篇
本篇将从四个方面对iOS开发中GCD的使用进行详尽的讲解: 一.什么是GCD 二.我们为什么要用GCD技术 三.在实际开发中如何使用GCD更好的实现我们的需求 一.Synchronous & ...
- iOS多线程之GCD详解
GCD(Grand Central Dispatch)是基于C语言开发的一套多线程开发机制.也是目前苹果官方推荐的多线程开发方法.iOS三种多线程开发中GCD是抽象层次最高的.当然用起来也是最简单的. ...
- ios 多线程小结----- GCD篇
//3 GCD(充分利用设备的多盒)-------------屏蔽了线程,只能看见任务 队列步骤两步,定制任务,将任务添加到队列.GCD将添加的任务,放到线程中去执行,自动执行,自动释放原则:先进先出 ...
- GCD下的几种实现同步的方式
GCD多线程下,实现线程同步的方式有如下几种: 1.串行队列 2.并行队列 3.分组 4.信号量 实例: 去网上获取一张图片并展示在视图上. 实现这个需求,可以拆分成两个任务,一个是去网上获取图片,一 ...
- 多线程&NSObject&NSThread&NSOperation&GCD
1.NSThread 每个NSThread对象对应一个线程,量级较轻(真正的多线程) 以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去关心线程的具体使用问题 2.NSOperation/NS ...
- 多线程 -- GCD
GCD中有2个核心概念 任务:执行什么操作 队列:用来存放任务 执行任务 同步方法: dispatch_sync dispatch_sync(dispatch_queue_t queue, dispa ...
随机推荐
- oracle order by 排序
Syntax ORDER BY { column-Name | ColumnPosition | Expression } [ ASC | DESC ] [ NULLS FIRST | NULLS L ...
- 最长公共前缀的golang实现
编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 "". 输入: ["flower","flow",&quo ...
- jquery clone
clone([Even[,deepEven]]) 概述 克隆匹配的DOM元素并且选中这些克隆的副本. 在想把DOM文档中元素的副本添加到其他位置时这个函数非常有用. 参数 EventsBooleanV ...
- 流程控制之for
for循环是 迭代式循环,其强大之处在于循环取值 用法一: l = [1, 2, 3, 4, 5, 5, 6, 5, 4, 3] for x in l: print(x) info = {'} for ...
- Mybatis集成(转)
文章转自http://blog.csdn.net/l454822901/article/details/51829653 什么是Mybatis MyBatis 本是apache的一个开源项目iBati ...
- MP实战系列(五)之封装方法讲解
mybatis plus封装的方法怎么用?以及它们对应的sql是那些sql?及其什么情况用? 这些需要说下,以下我将会将我常用的说下,不是常用的,可能提以下或者不提. 根据主键查询 UserEntit ...
- AI 循环神经网络
循环神经网络(Recurrent Neural Network,简称RNN),通常用于处理序列数据,正如卷积神经网络用于处理网格数据那样.
- <转>jmeter(十七)目录结构
之前了解过jmeter的目录结构,但只知道一些常用的配置文件,看到一篇介绍的比较详细的博客,就转载过来,当然,其实是自己懒得再去搜集更多资料慢慢看了,时间不够用... 原文链接:http://www. ...
- Volume is already attached by pod default/nginx-deployment-86dfb99868-szpkd. Status Running
1.部署WordPress - mysql ,想扩容,修改deployment,结果报错: MountVolume.SetUp failed for volume "pvc-e" ...
- 现有各种SSTC电路图,欢迎补充,研究,开发
现有各种SSTC电路图,欢迎补充,研究,开发 496464505 2016-3-1 16:01:55 现在的各种SSTC基本都是这些图 2016-3-3 19:28:23 comter2001 ...