概述


GCD是苹果异步执行任务技术,将应用程序中的线程管理的代码在系统级中实现。开发者只需要定义想要执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。由于线程管理是作为系统的一部分来实现的,因此可以统一管理,也可执行任务,这样比以前的线程更有效率。


GCD的使用


dispatch_sync与dispatch_async

  • dispatch_sync

    synchronous同步,一旦调用dispatch_sync方法,那么指定的处理(block)追加到指定Dispatch Queue中在执行结束之前该函数都不会返回,也就是说当前的线程会阻塞,等待dispatch_sync在指定线程执行完成后才会继续向下执行。

  • dispatch_async

    synchronous异步,一旦调用dispatch_async方法,那么指定的处理(block)追加到指定的Dispatch Queue中,dispatch_async不会做任何等待立刻返回,当前线程不受影响继续向下执行。

注意

使用dispatch_sync容易造成死锁,一般情况下应该使用dispatch_async,例如

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"1");
});
NSLog(@"2"); //这行代码不会输出

因为主线程等待dispatch_sync执行结束,而dispatch_sync又要在主线程中执行block。所以造成了死锁下面的代码不会执行。一般情况下应使用dispatch_async


Dispatch Queue

Dispatch Queue是执行处理的等待队列,通过Block把想要执行的处理追加到Dispatch Queue中,根据追加的顺序通过FIFO(先进先出)来执行处理。

有两种类型的队列,一种是Serial Dispatch Queue(串行队列)等待正在执行中的处理当处理结束时再执行队列中下一个处理。一种是Concurrent Dispatch Queue(并发队列)不等待现在执行中的处理。

Dispatch Queue种类 说明
Serial Dispatch Queue 等待现在执行中的处理结束
Concurrent Dispatch Queue 不等待现在执行中的处理结束

用代码详细说明两种队列

dispatch_async(queue,block0);
dispatch_async(queue,block1);
dispatch_async(queue,block2);
dispatch_async(queue,block3);
dispatch_async(queue,block4);

queueSerial Dispatch Queue时输出

block0
block1
block2
block3
block4

queueConcurrent Dispatch Queue时输出

block1
block0
block2
block4
block3
  • Serial Dispatch Queue

    当上面的queueSerial Dispatch Queue时按顺序执行,先执行block0执行结束后执行block1block2依次类推,因为是串行执行所以系统此时只开辟了一个线程来处理

  • Concurrent Dispatch Queue

    当上面的queueConcurrent Dispatch Queue时,因为不用等待执行中的处理结束,所以首先执行block0,不管block0是否结束都开始执行后面的block1,不等block1执行结束都执行block2依次类推。以为是并发执行,实际上是开辟了多个线程同时执行多个处理。

关于Concurrent Dispatch Queue线程问题

Concurrent Dispatch Queue中并行处理根据Dispatch Queue中的处理数量,机器CPU负载等一些状态来决定应该开辟多少个线程来处理。假如此时只能开辟三个线程但是要处理5个block事件系统应是下面这样处理:

线程0 线程1 线程2
block0 block1 block2
block3 block4

此时线程0处理block0,线程1处理block1,线程2处理block2。当block0执行玩后执行block3,可能此时block1还没有执行完,只有block1执行结束后才会执行block4。所以说并发队列执行的顺序是不确定的。


Main Dispatch Queue与Global Dispatch Queue

系统已经为我们提供了几种Dispatch Queue,不用我们去主动创建。Main Dispatch Queue把处理追加到当前的主线程RunLoop中执行。Global Dispatch Queue是把处理追加到一个Concurrent Dispatch Queue队列中处理。Global Dispatch Queue有四个优先级,系统提供的Dispatch Queue如下表所示:

名称 Dispatch Queue 的种类 说明
Main Dispatch Queue Serial Dispatch Queue 主线程执行
Global Dispatch Queue (High Priority) Concurrent Dispatch Queue 执行优先级(高)
Global Dispatch Queue (Default Priority) Concurrent Dispatch Queue 执行优先级(默认)
Global Dispatch Queue (Low Priority) Concurrent Dispatch Queue 执行优先级(低)
Global Dispatch Queue (Background Priority) Concurrent Dispatch Queue 执行优先级(后台)

系统提供的Dispatch Queue获取

dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t globalQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_t globalQueueDefau = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t globalQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t globalQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

dispatch_set_target_queue

自己通过dispatch_queue_creat函数创建的Dispatch Queue不管是串行队列还是并发的队列的优先级都是与Global Dispatch Queue的默认优先级相同,使用dispatch_set_target_queue可以变更Dispatch Queue的优先级

dispatch_set_target_queue使用

dispatch_queue_t serialQueue = dispatch_queue_create("com.test.serialQueue", NULL);
dispatch_queue_t globalQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_set_target_queue(serialQueue, globalQueueHigh);

上面的代码实现了通过dispatch_queue_creat创建的串行队列优先级默认变成了最高优先级,实现的效果是当有多个默认优先级的Serial Dispatch Queue并发执行时,如果设置了某一个Serial Dispatch Queue优先级为最高,那么先执行这个最高优先级的队列,然后再并发执行其他优先级相同的队列。


dispatch_after

如果想在某一时间后执行某一操作,实现定时器的效果可以用dispatch_after来实现

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC);
//从当前时间开始的10秒后
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"");
}); //将10秒后将要执行的操作追加到主线程进行执行

注意

上面的代码中dispatch_after并不是在10秒之后执行某一操作,而是在10秒后把要执行的操作追加到主线程中。比如主线程每0.01秒执一次RunLoop,那么这个追加操作最快10秒执行,最慢10+0.01秒执行。


Dispatch Group

上面介绍到如果使用Concurrent Dispatch Queue的话是不能确定队列中任务的执行顺序的,如果Concurrent Dispatch Queue中有三个任务要在这三个任务都执行结束后进行某个操作,这时就需要用到Dispatch Group

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, ^{
NSLog(@"block0");
});
dispatch_group_async(group, queue, ^{
NSLog(@"block1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"block2");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"Finish");
});

代码执行结果如下

block2
block1
block0
Finish

当追加到Dispatch Group中的处理全部结束时,dispatch_group_notify将会执行追加的Block。


dispatch_group_wait

dispatch_group_notify类似dispatch_group_wait也可以达到相同的效果

    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, ^{
NSLog(@"block0");
});
dispatch_group_async(group, queue, ^{
NSLog(@"block1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"block2");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"Finish");

执行结果如下

block2
block1
block0
Finish

dispatch_group_wait的效果是等待group追加的操作全部执行完后再执行下面的代码,第二个参数表示等待的时间,DISPATCH_TIME_FOREVER表示一直等待group的处理结果。直到处理完成才执行下面的代码。

如果只想等待一段指定的时间的话改变DISPATCH_TIME_FOREVER即可

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
if (result == 0) {
NSLog(@"Finish");
}
else{}

上面代码表示只等待1秒不管group中的处理是否全部完成都要执行下面的代码,当result = 0表示group中的处理已经处理完成,否则没有完成。


dispatch_barrier_async

如果在Concurrent Dispatch Queue中追加五个操作,这时想先并发执行前三个操作,等前三个操作都执行结束后再并发的执行后两个操作,这时就需要用到dispatch_barrier_async函数,具体实现如下:

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"block0");
});
dispatch_async(queue, ^{
NSLog(@"block1");
});
dispatch_async(queue, ^{
NSLog(@"block2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"barrier");
});
dispatch_async(queue, ^{
NSLog(@"block3");
});
dispatch_async(queue, ^{
NSLog(@"block4");
});

代码执行结果如下

block1
block0
block2
barrier
block3
block4

使用dispatch_barrier_async会等待在它之前追加到Concurrent Dispatch Queue中的所有操作都执行结束之后,再执行在它之后追加到Concurrent Dispatch Queue中的操作。

与Dispatch Group的区别

Dispatch Group是等待追加到它队列里面的所有操作执行结束。而dispatch_barrier_async是等待在它之前追加到它对列里面的操作。一个是等待队列执行结束,一个是等待队列中某些操作执行结束。


dispatch_apply

dispatch_apply是按照指定的次数把操作(block)追加到指定的Dispatch Group

示例1

dispatch_queue_t queueSerial = dispatch_queue_create("com.myProject.queueSerial", NULL);
dispatch_apply(5, queueSerial, ^(size_t index) {
NSLog(@"%zu",index);
});

运行结果

0
1
2
3
4

示例2

dispatch_queue_t queueCurrnt = dispatch_queue_create("com.myProject.queueCurrnt", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(5, queueCurrnt, ^(size_t index) {
NSLog(@"%zu",index);
});

运行结果

3
0
1
2
4

dispatch_apply第一个参数是要向队列中追加几次操作,第二个参数是将要追加操作的次数,第三个参数是用来区分第几次追加的操作。示例1中是向串行队列Serial Dispatch Queue追加操作。示例2中是向并发队列Concurrent Dispatch Queue追加操作。

dispatch_queue_create

在上面的示例中用到了dispatch_queue_create函数这个是用来创建Serial Dispatch QueueConcurrent Dispatch Queue。这个函数第一个参数是队列的标识符,标识符的写法最好按照域名倒写的方法来表示Dispatch Queue,这样方便在调试中查看。第二个参数表示创建队列的类型当为NULL表示创建串行队列,DISPATCH_QUEUE_CONCURRENT表示创建并行队列。


dispatch_suspend与dispatch_resume

如果再进行某个操作时,不想执行队列中的操作,在这个操作完成时再执行队列中的操作,这时用dispatch_suspend会挂起当前的队列,此时不会执行队列中追加的操作。而用dispatch_resume会恢复挂起的队列。dispatch_suspenddispatch_resume必须成对调用,有挂起就应该有恢复。

    dispatch_queue_t queue = dispatch_queue_create("com.myProject.queueCurrnt", NULL);
dispatch_suspend(queue);
dispatch_async(queue, ^{
for (unsigned int i = 0; i<10; i++) {
NSLog(@"Concurrent");
}
}); for (unsigned int i = 0; i<10; i++) {
NSLog(@"Serial");
}
dispatch_resume(queue);

如果没有dispatch_suspenddispatch_resume那么"Concurrent""Serial"会交替的输出,如果使用dispatch_suspend会把队列挂起然后执行下面的代码当"Serial"全部输出之后dispatch_resume恢复队列开始输出"Concurrent"


Dispatch Semaphore

当用Concurrent Dispatch Queue对数据库操作时容易发生数据竞争,当有100条数据要进行写入操作时,因为是并发操作如果此时正在写入第1条数据,同时第3条数据也要写入。这时程序就会发生错误,用Dispatch Semaphore可以解决这种问题,当然Dispatch Semaphore不仅仅局限于此。Dispatch Semaphore类似于信号等待,当对一个对象进行一项操作时,在这操作期间不允许其他操作来访问对象Dispatch Semaphore会设置一道屏障来阻止其他操作,到操作完成后向Dispatch Semaphore发送一个信号告诉它对象可以进行操作,然后开始进行下一操作,此时Dispatch Semaphore会再次屏蔽其他操作,直到收到对象操作完成的信号。

  • dispatch_semaphore_create
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

创建一个dispatch_semaphore_t类型的对象semaphore,这个就相当于上面所说的信号,它的参数用来判断是否需要等待。也就是上面所得是否屏蔽其他操作,当参数为0时,是等待。参数为1或者大于1时,是不等待。当参数为0时会一直等待直到收到信号,收到一次信号semaphore会自加1,这样semaphore大于或者等于1,所以等待取消会执行下面的操作。

  • dispatch_semaphore_wait
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

dispatch_semaphore_wait用来执行等待操作,当semaphore = 0dispatch_semaphore_wait开始等待阻止其他将要进行的操作。直到接受到信号,因为接受信号semaphore自加1所以dispatch_semaphore_wait取消等待。

注意

当执行dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);这行代码时,如果semaphore大于或者等于1,这行代码会自动将semaphore减去1。每运行一次semaphore - 1直到semaphore为0。

  • dispatch_semaphore_signal
dispatch_semaphore_signal(semaphore);

dispatch_semaphore_signal会让semaphore进行加1的操作。

Dispatch Semaphore代码示例

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    NSMutableArray *array = [NSMutableArray array];
/**
创建Dispatch Semaphore 并且设置semaphore为1
*/
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (unsigned int i = 0; i<100; i++) {
dispatch_async(queue, ^{
/**
因为semaphore为1所以dispatch_semaphore_wait不等待执行下面的操作
semaphore自减1此时semaphore==0 下一次操作将会等待,一直等到semaphore >= 1
*/
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); [array addObject:[[NSObject alloc] init]];
/**
上面添加数据执行完后dispatch_semaphore_signal操作会使semaphore加1
此时其他线程中的dispatch_semaphore_wait因为semaphore = 1 所以取消等待执行下面操作
*/
dispatch_semaphore_signal(semaphore);
}); }

dispatch_once

如果在应用中一段代码只想让它执行一次,那么就需要用到dispatch_once,一般用于创建单例。

    static NSObject *object = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
object=[[NSObject alloc] init];
});

本篇博文参考

Grand Central Dispatch(GCD)详解的更多相关文章

  1. Grand Central Dispatch(GCD)详解(转)

    概述 GCD是苹果异步执行任务技术,将应用程序中的线程管理的代码在系统级中实现.开发者只需要定义想要执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务.由于 ...

  2. Grand Central Dispatch (GCD)

    Grand Central Dispatch (GCD) Reference Grand Central Dispatch (GCD) comprises language features, run ...

  3. IOS 多线程编程之Grand Central Dispatch(GCD)介绍和使用 多线程基础和练习

    介绍:前面内容源自网络 Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统.这建立在任务并行执行的线程池模式 ...

  4. [转] iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用

    介绍: Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统.这建立在任务并行执行的线程池模式的基础上的.它首 ...

  5. iOS 多线程编程之Grand Central Dispatch(GCD)

    介绍: Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其它的对称多处理系统的系统.这建立在任务并行运行的线程池模式的基础上的. 它 ...

  6. iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用

    http://blog.csdn.net/totogo2010/article/details/8016129 GCD很好的博文

  7. NSThread 子线程 Cocoa NSOperation GCD(Grand Central Dispatch) 多线程

    单词:thread 英 θred:n 线.思路.vt 穿过.vi 穿透过 一.    进程.线程 进程:正在进行中的程序被称为进程,负责程序运行的内存分配,每一个进程都有自己独立的虚拟内存空间 线程: ...

  8. iOS开发-多线程之GCD(Grand Central Dispatch)

    Grand Central Dispatch(GCD)是一个强有力的方式取执行多线程任务,不管你在回调的时候是异步或者同步的,可以优化应用程序支持多核心处理器和其他的对称多处理系统的系统.开发使用的过 ...

  9. 在Swift中应用Grand Central Dispatch(上)转载自的goldenfiredo001的博客

    尽管Grand Central Dispatch(GCD)已经存在一段时间了,但并非每个人都知道怎么使用它.这是情有可原的,因为并发很棘手,而且GCD本身基于C的API在 Swift世界中很刺眼. 在 ...

随机推荐

  1. luogu2833 等式

    题目大意 给出\(a,b,c,x_1,x_2,y_1,y_2\),求满足\(ax+by+c=0\),且\(x\in[x1,x2],y\in [y1,y2]\)的整数解有多少对. 题解 用扩展欧几里得算 ...

  2. 常用框架(一):spring+springMvc+mybatis+maven

    项目说明: (1) 本例采用 maven web 工程做例子讲解 (2) 利用mybaits 提供的代码生成工具自动生成代码(dao接口,sql mapper映射文件,pojo数据库映射类) (3) ...

  3. iOS10 推送通知 UserNotifications

    简介 新框架 获取权限 获取用户设置 注册APNS,获取deviceToken 本地推送流程 远程推送流程 通知策略(Category+Action) 附件通知 代理回调 简介 iOS10新增了Use ...

  4. 线性回归模型之LinearRegression和SGDRegressor

    用美国波士顿的房价数据来介绍如何使用LR和SGDR模型进行预测 # 从sklearn.datasets导入波士顿房价数据读取器. from sklearn.datasets import load_b ...

  5. [专辑] 也晒晒我的RBAC系统 ——行一山人的博客

    也晒晒我的RBAC系统(一):概述 也晒晒我的RBAC系统(二):系统实现原理简介 也晒晒我的RBAC系统(三):后台管理程序源码及使用演示 也晒晒我的RBAC系统(四):框架源代码(超值奉献,请勿拍 ...

  6. node.js date-utils

    前端引用 <script type="text/javascript" src="date-utils.min.js"></script> ...

  7. Hadoop MapReduce编程 API入门系列之网页流量版本1(二十一)

    不多说,直接上代码. 对流量原始日志进行流量统计,将不同省份的用户统计结果输出到不同文件. 代码 package zhouls.bigdata.myMapReduce.areapartition; i ...

  8. nginx 集群简述

    1.负载均衡介绍: 负载均衡是由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须其他服务器的辅助.其工作模式为将外部发送来的请求均匀分配到对称结构中的 ...

  9. 第6章 服务模式 在 .NET 中实现 Service Gateway(服务网关)

    上下文 您正在设计企业应用程序,该程序需要使用由其他应用程序提供的服务.该服务定义了一个合约,所有服务使用者要访问该服务都必须遵守该合约.该合约定义了与此服务通信所需的技术.通信协议和消息定义等内容. ...

  10. Install opencv on Centos

    研究centos 有很长一段时间了,一直没有写过这方面的感觉,今天在看到网友的一篇文章时,结合亲身体会就下面安装opencv的一些步骤与大家共享. CentOS OpenCV已被广泛应用但是也在不断的 ...