原文链接:https://knightsj.github.io/2017/04/24/《Objective-C 高级编程》干货三部曲(三):GCD篇/
串行队列Serial Dispatch Queue:等待当前执行任务处理结束的队列
通过dispatch_queue_create函数可以创建队列,第一个函数为队列的名称,第二个参数是NULL
一旦开发者新建了一个串行队列,系统一定会开启一个子线程,所以在使用串行队列的时候,一定只创建真正需要创建的串行队列,避免资源浪费
并发队列Concurrent Dispatch Queue:不等待当前执行任务处理结束的队列
dispatch_queue_create函数第一个函数为队列的名称,第二个参数是DISPATCH_QUEUE_CONCURRENT。
******iOS和OSX基于Dispatch Queue中的处理数,CPU核数,以及CPU负荷等当前系统的状态来决定Concurrent Dispatch Queue中并发处理的任务数
现在我们知道dispatch_queue_create方法第一个参数指定了这个新建队列的名称,推荐使用逆序quan cheng全程域名(FQDN,fully qualified domain name)。这个名称可以在Xcode和CrashLog中显示出来,对bug的追踪很有帮助。
在继续讲解之前做个小总结,现在我们知道了:
如何创建串行队列和并发队列。
将任务追加到这两种队列里以后的执行效果。
将任务追加到多个串行队列会使这几个任务在不同的线程执行。
实际上,系统给我们提供了两种特殊的队列,分别对应串行队列和并发队列:
系统提供的队列:
Main Dispatch Queue
主队列:放在这个队列里的任务会追加到主线程的RunLoop中执行。需要刷新UI的时候我们可以直接获取这个队列,将任务追加到这个队列中。
Globle Dispatch Queue
全局并发队列:开发者可以不需要特意通过dispatch_queue_create方法创建一个Concurrent Dispatch Queue,可以将任务直接放在这个全局并发队列里面。
GCD的各种函数:
dispatch_set_target_queue
这个函数有两个作用:
改变队列的优先级。
防止多个串行队列的并发执行
改变队列的优先级
如果多个串行队列的优先级相同,那么循环执行的时候就会按照顺序执行,相当于串行
dispatch_after
dispatch_after解决的问题:某个线程里,在指定的时间后处理某个任务:
注意:不是在3秒之后处理任务,准确来说是3秒之后追加到队列。所以说,如果这个线程的runloop执行1/60秒一次,那么这个block最快会在3秒后执行,最慢会在(3+1/60)秒后执行。而且,如果这个队列本身还有延迟,那么这个block的延迟执行时间会更多
dispatch_group
如果遇到这样到需求:全部处理完多个预处理任务(block_1 ~ 4)后执行某个任务(block_finish),我们有两个方法:
如果预处理任务需要一个接一个的执行:将所有需要先处理完的任务追加到Serial Dispatch Queue中,并在最后追加最后处理的任务(block_finish)。
如果预处理任务需要并发执行:需要使用dispatch_group函数,将这些预处理的block追加到global dispatch queue中。
分别详细讲解一下两种需求的实现方式:
1、预处理任务需要一个接一个的执行:
这个需求的实现方式相对简单一点,只要将所有的任务(block_1 ~ 4 + block_finish)放在一个串行队列中即可,因为都是按照顺序执行的,只要不做多余的事情,这些任务就会乖乖地按顺序执行
2、先执行一部分,没有顺序要求,全部执行完后,在执行特定的任务
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (NSInteger index = 0; index <</span> 5; index ++) {
dispatch_group_async(group, queue, ^{
NSLog(@"任务%ld",index);
});
}
dispatch_group_notify(group, queue, ^{
NSLog(@"最后的任务");
});
因为这些预处理任务都是追加到global dispatch queue中的,所以这些任务的执行任务的顺序是不定的。但是最后的任务一定是最后输出的。
dispatch_group_notify函数监听传入的group中任务的完成,等这些任务全部执行以后,再将第三个参数(block)追加到第二个参数的queue(相同的queue)中。
假如异步任务再加异步的话就需要用
dispatch_group_enter
dispatch_group_wait
dispatch_group_wait 也是配合dispatch_group 使用的,利用这个函数,我们可以设定group内部所有任务执行完成的超时时间。
一共有两种情况:超时的情况和没有超时的情况:
一旦调用dispatch_group_wait以后,当经过了函数中指定的超时时间后 或者 指定的group内的任务全部执行后会返回这个函数的结果:
经过了函数中指定的超时时间后,group内部的任务没有全部完成,判定为超时,否则,没有超时
指定的group内的任务全部执行后,经过的时间长于超时时间,判定为超时,否则,没有超时。
也就是说:
如果指定的超时时间为DISPATCH_TIME_NOW,那么则没有等待,立即判断group内的任务是否完成。
可以看出,指定的超时时间为DISPATCH_TIME_NOW的时候相当于dispatch_group_notify函数的使用:判断group内的任务是否都完成。
dispatch_barrier_async
关于解决数据竞争的方法:读取处理是可以并发的,但是写入处理却是不允许并发执行的。
所以合理的方案是这样的:
读取处理追加到concurrent dispatch queue中
写入处理在任何一个读取处理没有执行的状态下,追加到serial dispatch queue中(也就是说,在写入处理结束之前,读取处理不可执行)。
我们看看如何使用dispatch_barrier_async来解决这个问题。前提是不能用全局队列和串行队列,只能用自定义的并行队列
dispatch_barrier_async就是在他之前的任务都执行完之后在执行他的,他执行完之后在执行他后面添加的任务,
dispatch_sync
到目前为止的所有例子都使用的是异步函数,有异步就一定会有同步,那么现在就来区分一下同步和异步函数的区别:
dispatch_async:异步函数,这个函数会立即返回,不做任何等待,它所指定的block“非同步地”追加到指定的队列中。
dispatch_sync:同步函数,这个函数不会立即返回,它会一直等待追加到特定队列中的制定block完成工作后才返回,所以它的目的(也是效果)是阻塞当前线程
因为是同步函数,它阻塞了当前线程(主线程),所以只能等到block内部的任务都结束后,才能打印下面的两行。
死锁就是在串行队列中添加了同步函数
dispatch_apply
通过dispatch_apply函数,我们可以按照指定的次数将block追加到指定的队列中。并等待全部处理执行结束
我们也可以用这个函数来遍历数组,取得下标进行操作
我们可以看到dispatch_apply函数与dispatch_sync函数同样具有阻塞的作用(dispatch_apply函数返回后才打印完毕)。
我们也可以在dispatch_async函数里执行dispatch_apply函数:
dispatch_suspend/dispatch_resume
挂起函数调用后对已经执行的处理没有影响,但是追加到队列中但是尚未执行的处理会在此之后停止执行。
1
2
dispatch_suspend(queue);
dispatch_resume(queue);
dispatch_once
通过dispatch_once处理的代码只执行一次,而且是线程安全的:
该函数主要用于单例模式的使用
网友评论