Grand Central Dispatch(GCD)详解(转)
概述
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);
当queue为Serial Dispatch Queue时输出
block0
block1
block2
block3
block4
当queue为Concurrent Dispatch Queue时输出
block1
block0
block2
block4
block3
Serial Dispatch Queue
当上面的queue为Serial Dispatch Queue时按顺序执行,先执行block0执行结束后执行block1、block2依次类推,因为是串行执行所以系统此时只开辟了一个线程来处理Concurrent Dispatch Queue
当上面的queue为Concurrent 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 Queue与Concurrent Dispatch Queue。这个函数第一个参数是队列的标识符,标识符的写法最好按照域名倒写的方法来表示Dispatch Queue,这样方便在调试中查看。第二个参数表示创建队列的类型当为NULL表示创建串行队列,DISPATCH_QUEUE_CONCURRENT表示创建并行队列。
dispatch_suspend与dispatch_resume
如果再进行某个操作时,不想执行队列中的操作,在这个操作完成时再执行队列中的操作,这时用dispatch_suspend会挂起当前的队列,此时不会执行队列中追加的操作。而用dispatch_resume会恢复挂起的队列。dispatch_suspend与dispatch_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_suspend与dispatch_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_waitsemaphore = 0时dispatch_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)详解(转)的更多相关文章
- Grand Central Dispatch(GCD)详解
概述 GCD是苹果异步执行任务技术,将应用程序中的线程管理的代码在系统级中实现.开发者只需要定义想要执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务.由于 ...
- Grand Central Dispatch (GCD)
Grand Central Dispatch (GCD) Reference Grand Central Dispatch (GCD) comprises language features, run ...
- IOS 多线程编程之Grand Central Dispatch(GCD)介绍和使用 多线程基础和练习
介绍:前面内容源自网络 Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统.这建立在任务并行执行的线程池模式 ...
- [转] iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用
介绍: Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统.这建立在任务并行执行的线程池模式的基础上的.它首 ...
- iOS 多线程编程之Grand Central Dispatch(GCD)
介绍: Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其它的对称多处理系统的系统.这建立在任务并行运行的线程池模式的基础上的. 它 ...
- iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用
http://blog.csdn.net/totogo2010/article/details/8016129 GCD很好的博文
- NSThread 子线程 Cocoa NSOperation GCD(Grand Central Dispatch) 多线程
单词:thread 英 θred:n 线.思路.vt 穿过.vi 穿透过 一. 进程.线程 进程:正在进行中的程序被称为进程,负责程序运行的内存分配,每一个进程都有自己独立的虚拟内存空间 线程: ...
- iOS开发-多线程之GCD(Grand Central Dispatch)
Grand Central Dispatch(GCD)是一个强有力的方式取执行多线程任务,不管你在回调的时候是异步或者同步的,可以优化应用程序支持多核心处理器和其他的对称多处理系统的系统.开发使用的过 ...
- 在Swift中应用Grand Central Dispatch(上)转载自的goldenfiredo001的博客
尽管Grand Central Dispatch(GCD)已经存在一段时间了,但并非每个人都知道怎么使用它.这是情有可原的,因为并发很棘手,而且GCD本身基于C的API在 Swift世界中很刺眼. 在 ...
随机推荐
- ValidateCode.cs验证码时设置缓存的使用
using System; using System.Collections.Generic; using System.Web; using System.Web.UI; using System. ...
- token验证 sae
在微信平台中修改服务器设置时,使用微信Demo的php,刚开始一直验证token 失败 解决办法 :在echo $echoStr;之前添加header('content-type:text');一句这 ...
- C#中启动外部应用程序
C#中我们可以通过Process类直接启动外部应用程序 代码如下: Process p = new Process(); p.StartInfo.FileName ...
- apache 2.4.9 配置其他客户端访问 required all granted
<Directory /> AllowOverride all #修改地方 Require all granted </Directory> # # Note that fro ...
- Java在ACM中的使用
1.基本框架 import java.oi.*; import java.util.* public class Main { public static void main(St ...
- Web Server PROPFIND Method internal IP Discosure
Title:Web Server PROPFIND Method internal IP Discosure --2012-11-09 09:47 Nessus扫描出来一个安全缺陷,Web Serv ...
- 布置第一个JBOSS服务器
还是要通过实践慢慢积累感觉.. SERVLET的制作,JAVAC的编译... package com.manning.jbia.intro; import java.io.IOException; i ...
- gis论坛
http://bbs.csdn.net/forums/GIS/ http://forums.mysql.com/list.php?23 http://www.remotegis.net/ http:/ ...
- Linux中.a,.la,.o,.so文件的意义和编程实现
Linux中.a,.la,.o,.so文件的意义和编程实现 Linux下文件的类型是不依赖于其后缀名的,但一般来讲: .o,是目标文件,相当于windows中的.obj文件 ...
- HDOJ 1194 Beat the Spread!(简单题)
Problem Description Superbowl Sunday is nearly here. In order to pass the time waiting for the half- ...