一 . GCD的简介:
Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统。这建立在任务并行执行的线程池模式的基础上的。而且是纯C语言,提供了非常多强大的函数。
二 . GCD的工作原理是:
让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。一个任务可以是一个函数(function)或者是一个block。 GCD的底层依然是用线程实现,不过这样可以让程序员不用关注实现的细节。
三 .GCD的优势:
GCD是苹果公司为多核的并行运算提出的解决方案;
GCD会自动利用更多的CPU内核(比如双核、四核);
GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程);
程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码;
温馨提示:
GCD存在于libdispatch.dylib这个库中,这个调度库包含了GCD的所有的东西,但任何IOS程序,默认就加载了这个库,在程序运行的过程中会动态的加载这个库,不需要我们手动导入。
四 .GCD的核心概念和使用步骤:
核心概念有2个:
- 队列:用来存放任务
- 任务:执行什么操作
使用步骤有2步:
- 定制任务
- 确定想做的事情
五. 同步异步线程
//同步线程
dispatch_sync(..., ^(block))
//异步线程
dispatch_async(..., ^(block))
六 . 队列(dispatch queue):
GCD中的FIFO队列称为dispatch queue(队列),它可以保证先进来的任务先得到执行。
队列分类
dispatch queue(队列)分为下面三种:
- 串行队列(Serial dispatch queues): 同时只执行一个任务,一个任务执行完毕以后,再执行下一个任务。串行队列通常用于同步访问特定的资源或数据。当你创建多个串行队列时,虽然它们各自是同步执行的,但串行队列与串行队列之间是并发执行的。
- 并发队列(Concurren dispatch queue): 可以并发地执行多个任务,让多个任务同时执行,自动开启多个线程同时执行任务,但是执行完成的顺序是随机的。并发功能只有在异步调用(dispatch_async)下有效。
- 主队列(Main dispatch queue): 它是全局可用的串行队列(直接在主线程中串行执行任务),是在应用程序主线程上执行任务的。
全局队列
系统提供四个全局并发队列,这四个队列对应不同的优先级,用户是不能够创建全局队列的,只能获取。获取函数名为dispatch_get_global_queue,函数原型为:dispatch_queue_t dispatch_get_global_queue(
long priority, unsigned long flags);
dipatch_queue_t queue;
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
dispatch_get_global_queue函数的第一个参数用来指定优先级, 第二个参数目前没有用处,是为将来备用的参数,值为0。
iOS 7.0 之前 优先级参数为:
DISPATCH_QUEUE_PRIORITY_HIGH 2 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
IOS 8.0系统提供的优先级参数为:
QOS_CLASS_USER_INTERACTIVE 0x21, 表示任务需要被立即执行提供好的体验,用来更新UI,响应事件等。这个等级最好保持小规模。
QOS_CLASS_USER_INITIATED 0x19, 表示任务由UI发起异步执行。适用场景是需要及时结果同时又可以继续交互的时候。
QOS_CLASS_DEFAULT 0x15, 默认(不是给程序员使用的,用来重置队列使用的)
QOS_CLASS_UTILITY 0x11, 表示需要长时间运行的任务,伴有用户可见进度指示器。经常会用来做计算,I/O,网络,持续的数据填充等任务。这个任务节能。
QOS_CLASS_BACKGROUND 0x09, 表示用户不会察觉的任务,使用它来处理预加载,或者不需要用户交互和对时间不敏感的任务。
QOS_CLASS_UNSPECIFIED 0x00, 未指定
自定义队列
用dispatch_queue_create函数可以创建自己定义的队列,该函数有两个参数,第一个是自定义的队列名,Apple建议我们使用倒置域名来命名队列,比如“com.dreamingwish.subsystem.task”。这些名字会在崩溃日志中被显示出来,也可以被调试器调用,这在调试中会很有用。第二个参数是队列类型,默认NULL或者DISPATCH_QUEUE_SERIAL的是串行,参数为DISPATCH_QUEUE_CONCURRENT为并行队列。
dispatch_queue_t queue
queue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue", DISPATCH_QUEUE_CONCURRENT);
主队列
主队列,是主线程中的唯一队列,而且是一个串行队列。主队列通过函数dispatch_get_main_queue获取,该函数原型为:dispatch_queue_t dispatch_get_main_queue();
这里写代码片
另外涉及到了三个函数
void dispatch_async(
dispatch_queue_t queue,
dispatch_block_t block);
dispatch_async 函数会将传入的block块放入指定的queue里运行。这个函数是异步的,这就意味着它会立即返回而不管block是否运行结束。因此,我们可以在block里运行各种耗时的操作(如网络请求) 而同时不会阻塞UI线程。
好的,知道这些特性之后,我们可以这样理解上面的代码:利用parameter变量异步地发起一个网络请求,并在请求之后更新UI线程。
dispatch_async开启一个异步操作,第一个参数是指定一个gcd队列,第二个参数是分配一个处理事物的程序块到该队列。
dispatch_set_target_queue的两个作用:
酷用来给新建的queue设置优先级:
Java代码 收藏代码
dispatch_queue_t serialQueue = dispatch_queue_create("com.oukavip.www",NULL);
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);
dispatch_set_target_queue(serialQueue, globalQueue);
/* * 第一个参数为要设置优先级的queue,第二个参数是参照物,既将第一个queue的优先级和第二个queue的优先级设置一样。
*/
需要注意的是,第一个参数是自定义的queue(默认优先级就是global queue的default),而不是系统的queue(global/main)。因为你不能给系统的queue设置权限。通过上面设置,serialQueue 就有了与globalQueue一样的优先级。其实这个函数不仅可以设置queue的优先级,还可以设置queue之间的层级结构。
酷修改用户队列的目标队列,使多个serial queue在目标queue上一次只有一个执行:
它会把需要执行的任务对象指定到不同的队列中去处理,这个任务对象可以是dispatch队列,也可以是dispatch源。而且这个过程可以是动态的,可以实现队列的动态调度管理等等。比如说有两个队列dispatchA和dispatchB,这时把dispatchA指派到dispatchB:
dispatch_set_target_queue(dispatchA, dispatchB);
那么dispatchA上还未运行的block会在dispatchB上运行。这时如果暂停dispatchA运行:
dispatch_suspend(dispatchA);
则只会暂停dispatchA上原来的block的执行,dispatchB的block则不受影响。而如果暂停dispatchB的运行,则会暂停dispatchA的运行。
demo:
一般都是把一个任务放到一个串行的queue中,如果这个任务被拆分了,被放置到多个串行的queue中,但实际还是需要这个任务同步执行,那么就会有问题,因为多个串行queue之间是并行的。这时使用dispatch_set_target_queue将多个串行的queue指定到了同一目标,那么着多个串行queue在目标queue上就是同步执行的,不再是并行执行。
Java代码 收藏代码
+(void)testTargetQueue {
dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_set_target_queue(queue3, targetQueue);
dispatch_async(queue1, ^{
NSLog(@"1 in");
[NSThread sleepForTimeInterval:3.f];
NSLog(@"1 out");
});
dispatch_async(queue2, ^{
NSLog(@"2 in");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"2 out");
});
dispatch_async(queue3, ^{
NSLog(@"3 in");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"3 out");
});
}
输出
1 in
1 out
2 in
2 out
3 in
3 out
(5) dispatch_queue_attr_make_with_qos_class
- 返回通过指定服务质量信息创建的队列的属性信息
【描述】
dispatch_queue_attr_t dispatch_queue_attr_make_with_qos_class (dispatch_queue_attr_t attire, dispatch_qos_class_t qos_class, int relative_priority);
【参数】
attr: 与特定的服务质量类相关联的队列的属性值信息。如果你想让被提交的任务被连续的执行,则指定DISPATCH_QUEUE_SERIAL值,或如果你想让被提交的任务被并发的执行,则指定DISPATCH_QUEUE_CONCURRENT值。如果你传NULL,则此方法默认创建一个连续的队列。
qos_class: 指定你的队列的服务质量的类值。这个上面说明过了,请查看第(2)条对dispatch_get_global_queue的介绍。
relative_priority: 一个负数,对那四个特定的服务质量优先级所代表的值的一个偏移,这个值必须小于0并且大于MIN_QOS_CLASS_PRIORITY。
【返回值】
一个属性值,如可被传入 dispatch_queue_create 方法作为参数创建一个队列
【讨论】
当你想要通过 dispatch_queue_create 方法创建一个你指定了服务质量的队列的时候,应该先调用一次本方法。本方法通过联合由你所指定的服务质量信息相关联的队列类型属性和返回一个你可传入 dispatch_queue_create 作为参数的值。使用本方法时,你所指定的服务质量的值优先于本队列本身内嵌的优先级。(翻译的优点狗血!大家自己看原文吧。)
全局队列的优先级与服务质量类的对应关系如下:
DISPATCH_QUEUE_PRIORITY_HIGH 对应于 QOS_CLASS_USER_INITIATED 类。
DISPATCH_QUEUE_PRIORITY_DEFAULT 对应于 QOS_CLASS_UTILITY 类。
DISPATCH_QUEUE_PRIORITY_LOW 对应于 QOS_CLASS_UTILITY 类附带一个-8的偏移。
DISPATCH_QUEUE_PRIORITY_BACKGROUND 对应于 QOS_CLASS_BACKGROUND 类。
【重要声明】
@import Dispatch
【有效性】
在iOS 8.0及以上版本可用