1 GCD 概要
1.1 什么是 GCD
GCD 是异步执行任务的技术之一。实现了极为复杂繁琐的多线程编程。
在导入 GCD 之前,Cocoa 框架提供了 NSObject 类的 performSelectorInBackground:withObject 方法 和 performSelectorOnMainThread 方法等简单的多线程技术。
2 GCD 的 API
2.1 Dispatch Queue
Dispatch Queue是执行处理的等待队列。按照追回的顺序(先进先出) 执行处理。
存在两种 Dispatch Queue:
一种:Serial Dispatch Queue
等待现在执行的处理结束 -> 执行顺序固定
另一种:Concurrent Dispatch Queue
不等待现在执行的处理结束 -> 执行顺序会根据处理内容和系统状态发生改变。
并行执行的处理数量取决于当前系统的状态。即系统中基于 Dispatch Queue 中的处理数、CPU 核数以及 CPU 负荷等当前系统的状态来决定 Concurrent Dispatch Queue 中并行执行的处理数。
所谓 “并行执行”,就是使用多个线程同时执行多个处理。
得到 Dispatch Queue 的两种方法:
第一种:通过 GCD 的 API 生成 Dispatch Queue。 (dispatch_queue_create)
第二种:获取系统标准提供的 Dispatch Queue
2.2 dispatch_queue_create
得到 Dispatch Queue 的两种方法:
第一种:通过 GCD 的 API 生成 Dispatch Queue。 (dispatch_queue_create)
用 dispatch_queue_create 可以生成多个 Dispatch Queue.
一旦生成 Serial Dispatch Queue 并追加处理,系统对于一个 Serial Dispatch Queue 就只生成并使用一个线程。有多少个 Serial Dispatch Queue 就生成多少个线程。
注:过多使用线程,就会消耗大量内存。
为了避免多线程编程问题之一 ——多个线程更新相同资源导致数据竞争时使用Serial Dispatch Queue.
实例:
dispatch_queue_t mySerialDispatchQueue =
dispatch_queue_create(“com.example.gcd.MySerialDispatchQueue”, NULL);
dispatch_queue_create 函数:
第一个参数:指定 Serial Dispatch Queue 的名称 推荐使用 -> 应用程序 ID 逆序全程域名。
第二个参数:指定为 NULL
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create(
“com.example.gcd.MyConcurrentDispatchQueue”, DISPATCH_QUEUE_CONCURRENT);
Dispatch Queue
必须由程序员负责释放。通过
dispatch_queue_create
函数生成的
Dispatch Queue
在使用结束后通过
dispatch_release
函数释放。
[现在 ARC 下 GCD 不需要 dispatch_release 了]
在 dispatch_async 函数中追加 Block 到 Dispatch Queue 后,即使立即释放 Dispatch Queue, 该 Dispatch Queue 由于被 Block 所持有也不会被废弃,因而 Block 能够执行。Block 执行结束后会释放 Dispatch Queue,这时谁都不持有 Dispatch Queue,因此它会被废弃。
2.3 Main Dispatch Queue / Global Dispatch Queue
第二种:获取系统标准提供的 Dispatch Queue
Main Dispatch Queue : 在主线程中执行。同 NSObject 类的 performSelectorOnMainThread 方法。
Global Dispatch Queue :是所有应用程序都能够使用的 Concurrent Dispatch Queue.
Global Dispatch Queue 有4个执行优先级:
将使用的 Global Dispatch Queue 的执行优先级作为线程的执行优先级使用。
各种 Dispatch Queue 的获取方式:
dispatch_get_main_queue();
dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
获取系统提供的 Main Dispatch Queue / Global Dispatch Queue 不需要内存管理。
2.4 dispatch_set_target_queue
dispatch_set_target_queue : 用于变更 Dispatch Queue 的执行优先级
dispatch_queue_create 函数生成的 Dispatch queue 都是 默认优先级(Default Priority)
dispatch_set_target_queue (参数1, 参数2);
参数1:要变更执行优先级的 Dispatch Queue
参数2:指定为要使用的执行优先级相同优先级的 Global Dispatch Queue
dispatch_set_target_queue 函数,不仅可以变量 Dispatch Queue 的执行优先级,还可以作成 Dispatch Queue 的执行阶层。
例:在必须将不可并行执行的处理追回到多个 Serial Dispatch Queue 中时,如果使用 dispatch_set_target_queue 函数将目标指定为某一个 Serial Dispatch Queue, 即可防止处理并行执行。
2.5 dispatch_after
dispatch_after:在指定时间后执行处理
注:dispatch_after 函数并不是在指定时间后执行处理,而只是在指定时间追回处理到 Dispatch Queue。
ull :unsigned long long
DISPATCH_TIME_NOW :表示现在的时间
获取 dispatch_time_t 对象的两种方法
第一种:dispatch_time 从第一个参数指定的时间开始,到第二个参数指定的毫微秒单位时间后的时间。
第二种:dispatch_wallTime 通常用于计算绝对时间,可以作为粗略的闹钟功能使用
2.6 Dispatch Group
Dispatch Group使用在什么情况下:
在追加到 Dispatch Queue 中的多个处理全部结束后想执行结束处理,这种情况下使用 Dispatch Group。
用 dispatch_group_create 函数生成的 dispatch_group_t 类型的 Dispatch Group 使用结束后需要通过 dispatch_release 函数释放
。[现在 ARC 会自动释放]
dispatch_group_async (group, queue, ^{…});
dispatch_group_async函数,追加 Block 到指定的 Dispatch Queue 中。第一个参数:指定生成的Dispatch Group, 指定的 Block 属于指定的 Dispatch Group.
为什么指定的 Block 属于指定的 Dispatch Group 以及 Dispatch Group 的释放问题
追加 Block 到 Dispatch Queue 的同时, Block 通过 dispatch_retain 函数持有 Dispatch Group,从而使得该 Block 属于 Dispatch Group。这样如果 Block 执行结束,该 Block 就通过 dispatch_release 函数释放持有的 Dispatch Group。 一旦 Dispatch Group 使用结束,不用考虑属于该 Dispatch Group 的 Block,立即通过 dispatch_release 函数释放即可。
无论向什么样的 Dispatch Queue 中追加处理,使用 Dispatch Group 都可监视这些处理执行的结束。一旦检测到所有处理执行结束,就可将结束的处理追加到 Dispatch Queue 中。
在 Dispatch Group 中使用的函数:
dispatch_group_notify :
dispatch_group_notify (group, dispatch_get_main_queue(), ^{…});
在追加到 Dispatch Group 中的处理全部执行结束时,dispatch_group_notify 函数会将执行的 Block 追回到 Dispatch Queue 中。
dispatch_group_wait : 等待全部处理执行结束。
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
第二个参数:指定为等待的时间(超时),属于 dispatch_time_t 类型的值
返回值:0 与 非0
返回值不为0: 虽然经过了指定的时间,但属于 Dispatch Group 的某一个处理还在执行中。
返回0: 全部处理执行结束。
“等待”是什么意思:
一旦调用 dispatch_group_wait 函数,该函数就处于调用的状态而不返回。即执行 dispatch_group_wait 函数的现在的线程停止。在经过 dispatch_group_wait 函数中指定的时间或属于指定 Dispatch Group 的处理全部执行结束之前,执行该函数的线程停止。
2.7 dispatch_barrier_async
dispatch_barrier_async用于对文件写入数据
dispatch_barrier_async与 Concurrent Dispatch Queue 一起使用, 可实现高效率的数据库访问和文件访问
执行结果:
dispatch_barrier_async 函数会等待追回到 Concurrent Dispatch Queue 上的并行执行的处理全部结束之后,再将指定的处理追回到该 Concurrent Dispatch Queue 中。然后在由 dispatch_barrier_async 函数追回的处理执行完毕后, Concurrent Dispatch Queue 才恢复为一般的动作,追回到该 Concurrent Dispatch Queue 的处理又开始并执行。
2.8 dispatch_sync
dispatch_sync :将指定的 Block “同步” 追回到指定的 Dispatch Queue 中。在追回 Block 结束之前,dispatch_sync 函数会一直等待。
什么情况下使用 dispatch_sync ?
例如:执行 Main Dispatch Queue 时,使用另外的线程 Global Dispatch Queue 进行处理,处理结束后立即使用所得到的结果。
一旦调用 dispatch_sync 函数,那么在指定的处理执行结束之前,该函数不会返回。dispatch_sync 函数可简化源代码,也可说是简易版的 dispatch_group_wait 函数。
dispatch_sync函数容易引起死锁
例如:在主线程中执行以下代码就会死锁
该源代码在 Main Dispatch Queue 即主线程中执行指定的 Block,并等待其执行结束。而其实在主线程中正在执行这些源代码,所以无法执行追加到 Main Dispatch Queue 的 Block。
2.9 dispatch_apply
dispatch_apply :是 dispatch_sync 函数和 Dispatch Group 的关联 API。该函数按指定的次数将指定的 Block 追回到指定的 Dispatch Queue 中,并等待全部处理执行结束。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
//size_t : unsigned long; %zu用来输出 size_t 类型
NSLog(@"%zu", index);
});
NSLog(@“done");
输出结果中最后的 done 必定在最后的位置上。这是因为 dispatch_apple 函数会等待全部处理执行结果。
dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t))
参数1:重复次数
参数2:追加对象的 Dispatch Queue
参数3:追加的处理 Block,Block 为带参数的 Block,为了按第1个参数重复追加 Block 并区分各个 Block 而使用。
2.10 dispatch_suspend / dispatch_resume
dispatch_suspend :挂起指定的 Dispatch Queue
dispatch_resume :恢复指定的 Dispatch Queue
这些函数对已经执行的处理没有影响。挂起后,追加到 Dispatch Queue 中但尚未执行的处理在此之后停止执行。而恢复则使得这些处理能够继续执行。
2.11 Dispatch Semaphore
Dispatch Semaphore是持有计数的信号,该计数是多线程编程中的计数类型信号。计数为0时等待,计数为1或大于1时,减去1而不等待。
2.12 dispatch_once
dispatch_once:在单例模式,生成单例对象时使用。
2.13 Dispatch I/O
Dispatch I/O读写文件时使用