GCD 文档的查看
GCD 最全的资料不在百度,不在 api ,在 mac 终端。
打开终端
$ man dispatch
// 退出当前界面
$ q
see also
我们可以通过输入 man dispatch_after
查看相应的文档。
// dispatch_after 函数的文档
$ man dispatch_after
// dispatch_async 函数的文档
$ man dispatch_async
1、队列同步任务和异步任务在项目中的使用
假设: a 为登录, b, c 为不同的下载任务。
下面这样的操作就可以保证,只有在登录的情况下才可以进行 下载任务。
// 创建一个并发队列
dispatch_queue_t q = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);
// 并发队列同步执行
// 不会开线程,会在当前线程执行
// 同步执行任务 - 作用可以让一些异步执行的任务"依赖" 某一个特殊的任务
dispatch_sync(q, ^{
NSLog(@"a: %@",[NSThread currentThread]);
});
// 永远都是在 a 执行完毕后,在回执行 b c ,但是 b , c 执行的顺序是不定的。
// 并发队列异步执行
// 会开线程,在子线程运行
dispatch_async(q, ^{
NSLog(@"b: %@",[NSThread currentThread]);
});
dispatch_async(q, ^{
NSLog(@"c: %@",[NSThread currentThread]);
});
打印结果:
2016-06-21 12:22:01.900 Thread-Objc[44983:2486117] a: <NSThread: 0x7f8762501ba0>{number = 1, name = main}
2016-06-21 12:22:01.900 Thread-Objc[44983:2486394] c: <NSThread: 0x7f876257ac90>{number = 3, name = (null)}
2016-06-21 12:22:01.900 Thread-Objc[44983:2486385] b: <NSThread: 0x7f8762717f20>{number = 2, name = (null)}
2、队列迭代
提交一个任务到队列进行多次执行
/*
size_t iterations : 任务执行的次数
dispatch_queue_t queue : 要执行任务的队列
void (^block)(size_t) : 要执行的任务
*/
void
dispatch_apply(size_t iterations, dispatch_queue_t queue,
void (^block)(size_t));
// 上面函数是下面的封装。
void
dispatch_apply_f(size_t iterations, dispatch_queue_t queue,
void *context,
void (*work)(void *, size_t));
如果 dispatch_queue_t queue 的队列是并发队列。任务是并发执行的, 它必须是可重入的安全。
dispatch_apply( ) 是同步执行的。 意味着会阻塞当前线程。和 for 循环一样只有在迭代完成后才会返回。如果需要异步执行,就需要创建一个新的 队列,调用 dispatch_async() 函数,再在 dispatch_async() 函数中调用 dispatch_apply( )。
示例:
NSLog(@"before: %@", [NSThread currentThread]);
__block int x = 0;
// q 是串行或者并行没有多大的关系
dispatch_queue_t q = dispatch_queue_create("q", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(q, ^{
// q1 是串行的,就只开1条线程,并发的就开多条线程。线程数由 GCD 决定。
dispatch_queue_t q1 = dispatch_queue_create("q1", DISPATCH_QUEUE_SERIAL);
size_t iterations = 10;
dispatch_apply( iterations, q1, ^(size_t idx ) {
NSLog(@"size_t : %zd --- x : %zd -- %@", idx, x, [NSThread currentThread]);
});
});
NSLog(@"after: %@", [NSThread currentThread]);
在主线程中调用 dispatch_apply( ) 传入的是主队列,会造成死锁。
文档中说:
dispatch_apply() 是 dispatch_async() 和 semaphore (完成信号)的包装;
(我特么真没看出来)
dispatch_apply() 和 dispatch_async() 不一样的地方是,block 提交到 dispatch_apply() 是相对独立的,或者是依赖于已经完成但是 index 比自己小的 block 。
这个函数就像 for 循环一样提供数据级并发。 相对于 for 循环(for 循序操作都是在当前线程),并发迭代 的效率更加的高。
使用前提条件:
本次的迭代执行的 block 和上一次迭代执行的 block 不能有任何的关系。
(每次 block 的迭代执行是相对独立的!。 如果说执行的 block 之间有依赖关系,那你还是放弃吧!)
如果你使用循环执行固定次数的迭代, 并发dispatch queue可能会提高性能。
- 如果每次迭代执行的任务与其它迭代独立无关,而且循环迭代执行顺序也无关紧要的话,你可以调用dispatch_apply或dispatch_apply_f函数来替换循环。
普通 for 循环
NSLog(@"before: %@", [NSThread currentThread]);
int x = 0;
for (int index = 0 ; index < 10; index ++) {
NSLog(@"task: %@ --- index : %zd ---- x : %zd", [NSThread currentThread], index , x );
x++;
}
NSLog(@"after: %@", [NSThread currentThread]);
打印结果:
2016-06-23 14:55:04.706 Thread-Objc[12389:1352331] before: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}
2016-06-23 14:55:04.707 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main} --- index : 0 ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)} --- index : 1 ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352381] task: <NSThread: 0x7f97f2635530>{number = 3, name = (null)} --- index : 2 ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352375] task: <NSThread: 0x7f97f2635970>{number = 4, name = (null)} --- index : 3 ---- x : 0
2016-06-23 14:55:04.708 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main} --- index : 4 ---- x : 1
2016-06-23 14:55:04.708 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)} --- index : 5 ---- x : 2
2016-06-23 14:55:04.708 Thread-Objc[12389:1352381] task: <NSThread: 0x7f97f2635530>{number = 3, name = (null)} --- index : 6 ---- x : 3
2016-06-23 14:55:04.709 Thread-Objc[12389:1352375] task: <NSThread: 0x7f97f2635970>{number = 4, name = (null)} --- index : 7 ---- x : 4
2016-06-23 14:55:04.709 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main} --- index : 8 ---- x : 5
2016-06-23 14:55:04.710 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)} --- index : 9 ---- x : 6
2016-06-23 14:55:04.721 Thread-Objc[12389:1352331] after: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}
GCD 迭代
NSLog(@"before: %@", [NSThread currentThread]);
size_t iterations = 10;
__block int x = 0;
dispatch_apply( iterations , dispatch_get_global_queue(0, 0), ^(size_t idx) {
NSLog(@"task: %@ --- index : %zd ---- x : %zd", [NSThread currentThread], idx , x );
x++;
});
NSLog(@"after: %@", [NSThread currentThread]);
打印结果
2016-06-23 14:55:04.706 Thread-Objc[12389:1352331] before: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}
2016-06-23 14:55:04.707 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main} --- index : 0 ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)} --- index : 1 ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352381] task: <NSThread: 0x7f97f2635530>{number = 3, name = (null)} --- index : 2 ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352375] task: <NSThread: 0x7f97f2635970>{number = 4, name = (null)} --- index : 3 ---- x : 0
2016-06-23 14:55:04.708 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main} --- index : 4 ---- x : 1
2016-06-23 14:55:04.708 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)} --- index : 5 ---- x : 2
2016-06-23 14:55:04.708 Thread-Objc[12389:1352381] task: <NSThread: 0x7f97f2635530>{number = 3, name = (null)} --- index : 6 ---- x : 3
2016-06-23 14:55:04.709 Thread-Objc[12389:1352375] task: <NSThread: 0x7f97f2635970>{number = 4, name = (null)} --- index : 7 ---- x : 4
2016-06-23 14:55:04.709 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main} --- index : 8 ---- x : 5
2016-06-23 14:55:04.710 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)} --- index : 9 ---- x : 6
2016-06-23 14:55:04.721 Thread-Objc[12389:1352331] after: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}
对比发现 x 的值在 GCD 迭代的过程中是凌乱的。
当符合前提条件,GCD 的迭代的效率更高。
3、队列的暂停和启动操作
给目标队列添加管理对象
/*
dispatch_object_t object : 队列管理对象
dispatch_queue_t queue : 被管理的队列
*/
void
dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);
调度对象的相关知识
调度对象是通过共享函数去协调内存管理,暂停,取消,上下文指针。
-
挂起操作
在调度队列或者调度源被触发的 blocks 能够通过分别执行dispatch_suspend()
和dispatch_resume()
函数进行挂起和启动操作。另一些调度对象是不支持挂起操作的。
这个调度框架总是在执行 block 之前检查 block 的悬挂状态。一个 block 在执行期间对 block 做的改变是不会产生效果的。因此一个对象的挂起操作是异步的,除非它是通过执行上下文从目标队列获取的对象。把一个不是调度队列或调度源的对象执行挂起操作或启用操作的操作结果是没有定义的。
重点:
悬挂操作适用于调度对象生命周期的任何时候。包括 finalizer 函数,取消回调。 挂起操作会使计数器加1 ,启用操作会使计数器减一。因此,dispatch_suspend()
和dispatch_resume()
进行成对调用,以保证对象最终能够释放。一个调度对象在所有的引用释放后进行挂起操作的操作结果是没有定义的。
-
上下文指针
调度对象支持补充(追加)上下文指针。 上下文指针的值能够分别通过dispatch_get_context()
和dispatch_set_context()
函数进行获取和更新。 当调度对象所有的引用全部释放的时候,如果上下文指针是非空的,通过dispatch_set_finalizer_f()
能给每一个可选的对象指定一个可以异步调用的终结函数。给应用程序一个可以给调度自由关联上下文数据的机会。这个终结器将要运行在和 调度对象绑定的目标队列上。