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 ...
随机推荐
- Jenkins的配置从节点中默认没有Launch agent via Java Web Start,该如何配置使用
Jenkins的配置从节点中默认没有Launch agent via Java Web Start,如下图所示,而这种启动方式在Windows上是最方便的. 如何设置才能让出来呢? 1:打开" ...
- k-vim安装及The ycmd server SHUT DOWN (restart with ':YcmRestartServer')这种错误的解决方法
vim配置 下载地址:https://github.com/wklken/k-vim 安装步骤: 1. clone 到本地 git clone https://github.com/wklken/k- ...
- [MapReduce_add_5] MapReduce 实现标签的生成与聚合
0. 说明 MapReduce 实现标签的生成与聚合 介绍 && 流程图 && 程序编写 1. 介绍 [1.1 原始有效数据] 86913510 {"revi ...
- linux之在当前目录下按照文件大小进行排序的三种方法
当前目录下按照文件大小排序 [root@test23 script]# ls -lSh 总用量 44K -rw-r--r-- 1 root root 2.4K 12月 8 17:24 test.con ...
- PyQt5--ButtonDrag
# -*- coding:utf-8 -*- ''' Created on Sep 21, 2018 @author: SaShuangYiBing Comment: ''' import sys f ...
- 关于new
JS通过new关键字,可以调用相关的 构造方法 生成一个 对象 ,function ---> object
- Problem UVA12657-Boxes in a Line(数组模拟双链表)
Problem UVA12657-Boxes in a Line Accept: 725 Submit: 9255 Time Limit: 1000 mSec Problem Description ...
- Http实现文件下载
HttpServlet实现下载文件,重要的设置header,否则浏览器无法解析为下载. Header示例: 1:response.setContentType("application/pd ...
- slf4j+logback搭建超实用的日志管理模块
文章转自http://www.2cto.com/kf/201702/536097.html slf4j+logback搭建超实用的日志管理模块(对日志有编号管理):日志功能在服务器端再常见不过了,我们 ...
- P1734 最大约数和
题目描述 选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大. 输入输出格式 输入格式: 输入一个正整数S. 输出格式: 输出最大的约数之和. 输入输出样例 输入样例#1: 复 ...