前言
什么是GCD?
Grand Central dispatch(GCD)是异步执行任务的技术之一。一般将应用程序中记述的线程管理用的代码在系统级中实现。开发者只需要定义想执行的任务并追加到适当的dispatch Queue中,GCD就能生成必要的线程并计划执行任务。由于线程管理是作为系统的一部分来实现的,因此可以统一管理,也可执行任务,这样就比以前的线程更加有效率。
什么是线程?
- 线程(thread)是操作系统能够进行运算调度的最小单位。
- Mac、iPhone的操作系统OS X、iOS根据用户的指示启动应用程序后,首先便将包含在应用程序中的CPU命令列配置到内存中。CPU从应用程序指定的地址开始,一个一个地执行CPU命令列。由于一个CPU一次只能执行一个命令,不能执行某处分开的并列的两个命令,因此通过CPU执行的CPU命令列就是一条路走到黑。这里所说的“1个CPU执行的CPU名列为一条路走到黑”就是“线程”。
- 现在一个物理的CPU芯片实际上有64(64核)个CPU,即可以拥有多条线程。
- 多线程编程会产生很多编程技术问题:数据竞争、死锁、内存消耗等问题。
- 想要更加了解线程、进程等可自行参阅《操作系统原理》这本书。
dispatch Queue
dispatch Queue是执行处理的等待队列。我们可以调用dispatch_async函数等API,在Block语法中记述想执行的处理并追加到dispatch Queue中。dispatch Queue会按照FIFO(先进先出)执行处理。下面的图解释了FIFO
在dispatch Queue中存在两种dispatch Queue,一种是等待现在执行中的Serial dispatch Queue,另一种是不等待直接执行的Concurrent dispatch Queue。
dispatch Queue种类 | 说明 |
---|---|
Serial dispatch Queue | 等待现在执行中的处理结束后才开始执行 |
Concurrent dispatch Queue | 不等待现在执行中的处理,直接开始执行 |
因为Serial dispatch Queue是等待执行完成后才开始下一个处理。那么在有多个处理的时候,也是按照先后顺序来的。
int main(int argc, const char * argv[]) {
// 创建一个SerialQueue
dispatch_queue_t serialdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
// Serial dispatch Queue
dispatch_async(serialdispatchQueue, ^{printf("1\n");});
dispatch_async(serialdispatchQueue, ^{printf("2\n");});
dispatch_async(serialdispatchQueue, ^{printf("3\n");});
dispatch_async(serialdispatchQueue, ^{printf("4\n");});
// 防止程序结束
while (1) {
}
return 0;
}
执行结果如下:
Concurrent dispatch Queue是不等待执行,那么任何任务添加到Concurrent dispatch Queue后,就会立即执行。但是Concurrent dispatch Queue不是无限制的立即执行当前添加的处理,当前并行执行的处理的数量取决于当前系统的状态。即iOS和OS X基于dispatch Queue中的处理数、CPU数、以及CPU负荷等当前系统状态来决定Concurrent dispatch Queue中并行处理数。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
// 创建一个Concurrent dispatch Queue
dispatch_queue_t concurrentdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_CONCURRENT);
// Serial dispatch Queue
dispatch_async(concurrentdispatchQueue, ^{NSLog(@"1");});
dispatch_async(concurrentdispatchQueue, ^{NSLog(@"2");});
dispatch_async(concurrentdispatchQueue, ^{NSLog(@"3");});
dispatch_async(concurrentdispatchQueue, ^{NSLog(@"4");});
dispatch_async(concurrentdispatchQueue, ^{NSLog(@"5");});
dispatch_async(concurrentdispatchQueue, ^{NSLog(@"6");});
// 防止程序结束
while (1) {
}
return 0;
}
执行结果:
其中执行结果乱序是因为处理添加到线程中的时候,需要等待线程执行完成后,才开始执行。即6个Block添加到6个线程中,6个线程里面都仍然有任务,等任务执行完成后,才开始执行我们添加的处理。
dispatch_queue_create
在Dispatch Queue中,我们使用了dispatch_queue_create
来建立一个Queue。
生成Dispatch Queue有两种方法:
- 通过GCD的API生成dispatch Queue
- 获取系统标准提供的dispatch Queue
这里先说明第一种方法,第二种在Main dispatch Queue/Global dispatch Queue中说明。
使用dispatch_queue_create
// 创建一个Concurrent dispatch Queue
dispatch_queue_t concurrentdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_CONCURRENT);
// 创建一个Serial dispatch Queue
dispatch_queue_t serialdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
dispatch_queue_create
函数有2个参数,第一个参数是dispatch Queue的名称,推荐使用应用程序ID这样的逆序全程域名。该名称在Xcode和Instruments的调试器中作为dispatch Queue的名称表示。第二个参数是指定dispatch Queue的类型,如果填写NULL则默认为DISPATCH_QUEUE_SERIAL。
前文讲到,Serial dispatch Queue同时只能执行一个处理。但是生成多个Serial dispatch Queue时,各个Serial dispatch Queue将并行执行。4个处理追加到4个Serial dispatch Queue中,4个处理将同时执行。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
// 创建一个SerialQueue
dispatch_queue_t serialdispatchQueue1 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialdispatchQueue2 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialdispatchQueue3 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
// Serial dispatch Queue
dispatch_async(serialdispatchQueue1, ^{NSLog(@"1");});
dispatch_async(serialdispatchQueue2, ^{NSLog(@"2");});
dispatch_async(serialdispatchQueue3, ^{NSLog(@"3");});
// 防止程序结束
while (1) {
}
return 0;
}
执行结果:
由执行结果乱序可以知道,多个Serial dispatch Queue是并行执行的!
dispatch_queue_create使用注意
- 不能无限制的创建Serial Dispatch Queue,会消耗大量的内存,引起大量的上下文切换,大幅度降低系统的响应性能。
- 在为了避免多个线程对同一个资源进行操作时(数据竞争)使用Serial Dispatch Queue,因为其使用一个线程,数据安全。
- 当不需要顾忌数据竞争问题时候,推荐使用Concurrent Dispatch Queue。因为不管生成多少,系统会对其进行管理,不用担心Serial Dispatch Queue类似的问题。
- 最好为每一个Dispatch Queue编写不同的名字,否则你会在调试多线程程序的时候感觉自己仿佛是一个辣鸡。
- 关于
dispatch_retain
和dispatch_release
的使用- 如果你部署的最低目标低于 iOS 6.0 or Mac OS X 10.8,你应该自己管理GCD对象,使用(dispatch_retain,dispatch_release),ARC并不会去管理它们。
- 如果你部署的最低目标是 iOS 6.0 or Mac OS X 10.8或者更高,ARC已经能够管理GCD对象了,这时候,GCD对象就如同普通的OC对象一样,不应该使用dispatch_retain或者dispatch_release。
Main Dispatch Queue/Global Dispatch Queue
除了使用dispatch_queue_create
,还可以利用系统标准提供的Dispatch Queue。系统提供了2个:
- Main Dispatch Queue,即主线程中执行的Dispatch Queue,而主线程只有一个,所以Main Dispatch Queue就是Serial Dispatch Queue.
- Global Dispatch Queue,是所有应用程序都能够使用的Concurrent Dispatch Queue。所以没有必要通过
dispatch_queue_create
来创建,直接获取Global Dispatch Queue即可。
/** Main Dispatch Queue的获取 */
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();
/** Global Dispatch Queue(最高)的获取方法 */
dispatch_queue_t highGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
/** Global Dispatch Queue(默认)的获取方法 */
dispatch_queue_t defaultGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/** Global Dispatch Queue(低)的获取方法 */
dispatch_queue_t losGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
/** Global Dispatch Queue(后台,最低)的获取方法 */
dispatch_queue_t backgroundGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
Global Dispatch Queue
Global Dispatch Queue有4个优先级,分别是高优先级、默认优先级、低优先级和后台优先级。
名称 | Dispatch Queue的种类 | 说明 |
---|---|---|
Main Dispatch Queue | Serial 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 | 执行优先级:后台(最低) |
结语
- 本文主要讲常用的一些GCD的API,更多内容,请等待下一周更新。
- 此为《Objective-C 高级编程》的学习笔记。
- 如有错误,欢迎指正。
- 如需转载,请注明出处。