作者Talent•C 转载请注明出处
前言 上篇文章主要以一些理论知识为主,主要介绍了 GCD 相关概念及简单的API用法,本篇文章主要以实践操作为主,主要介绍 dispatch group 与 dispatch apply 。
下面我们正式开始今天的任务。。。
Group的使用 相关API介绍1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
dispatch_group_create();
dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue, dispatch_block_t block);
dispatch_group_async_f(dispatch_group_t group, dispatch_queue_t queue,void *_Nullable context,dispatch_function_t work);
dispatch_group_enter(dispatch_group_t group);
dispatch_group_leave(dispatch_group_t group);
dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue, dispatch_block_t block);
dispatch_group_notify_f(dispatch_group_t group,dispatch_queue_t queue,void *_Nullable context,dispatch_function_t work);
以上API基本涵盖了 GCD Group 的所有函数,下面我们来用他们实现一些具体功能。1、分组执行一系列任务执行结果汇总输出 例如并发执行任务1~任务5最后将五个任务的总结果输出实现方式一 自动调度组 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
创建 并发队列talentQueue
参数 "TalentC.dispatch.queue.test" 队列的标记 可自定义
参数 DISPATCH_QUEUE_CONCURRENT并发队列 (同时可选则 DISPATCH_QUEUE_SERIAL 串行队列)
返回 dispatch_queue_t 队列对象 dispatch_object
*/
dispatch_queue_t talentQueue = dispatch_queue_create("TalentC.dispatch.queue.test" , DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t talentGroup = dispatch_group_create();
for (int i = 1 ; i <= 5 ; i ++) {
dispatch_group_async(talentGroup, talentQueue, ^{
sleep(3 );
NSString *string = [NSString stringWithFormat:@"任务%d" ,i];
NSLog (@"%@ 是否主线程:%@" ,string,[NSThread currentThread].isMainThread?@"YES" :@"NO" );
});
}
dispatch_group_notify(talentGroup, talentQueue, ^{
NSLog (@"thread: %p 是否主线程?:%@ 任务全部执行完毕*********" ,[NSThread currentThread],[NSThread currentThread].isMainThread?@"YES" :@"NO" );
});
NSLog (@"分组任务添加完毕 是否主线程:%@" ,[NSThread currentThread].isMainThread?@"YES" :@"NO" );
执行结果:
2017-04-06 15:28:16.692 GCD-Test[17383:8092341] 分组任务添加完毕 是否主线程:YES 2017-04-06 15:28:19.698 GCD-Test[17383:8092364] 任务1 是否主线程:NO 2017-04-06 15:28:19.698 GCD-Test[17383:8092365] 任务2 是否主线程:NO 2017-04-06 15:28:19.699 GCD-Test[17383:8092358] 任务3 是否主线程:NO 2017-04-06 15:28:19.699 GCD-Test[17383:8092368] 任务4 是否主线程:NO 2017-04-06 15:28:19.699 GCD-Test[17383:8092369] 任务5 是否主线程:NO 2017-04-06 15:28:19.699 GCD-Test[17383:8092369] thread: 0x12ed21b50 是否主线程?:NO 任务全部执行完毕*
注意: 这里的执行顺序不一定按照1~5执行因为 talentQueue 因为是并发线程如果为串行线程则会按照1~5的顺序进行。
实现方式二 手动调度组 只需要将上述中的 dispatch_group_async
修改成 dispatch_group_enter
+ dispatch_async
+ dispatch_group_leave
的组合即可,代码如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
创建 并发队列talentQueue
参数 "TalentC.dispatch.queue.test" 队列的标记 可自定义
参数 DISPATCH_QUEUE_CONCURRENT 并发队列 (同时可选则 DISPATCH_QUEUE_SERIAL 串行队列)
返回 dispatch_queue_t 队列对象 dispatch_object
*/
dispatch_queue_t talentQueue = dispatch_queue_create("TalentC.dispatch.queue.test" , DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t talentGroup = dispatch_group_create();
for (int i = 1 ; i <= 5 ; i ++) {
dispatch_group_enter(talentGroup);
dispatch_async (talentQueue, ^{
sleep(3 );
NSString *string = [NSString stringWithFormat:@"任务%d" ,i];
NSLog (@"%@ 是否主线程:%@" ,string,[NSThread currentThread].isMainThread?@"YES" :@"NO" );
dispatch_group_leave(talentGroup);
});
}
dispatch_group_notify(talentGroup, talentQueue, ^{
NSLog (@"thread: %p 是否主线程?:%@ 任务全部执行完毕*********" ,[NSThread currentThread],[NSThread currentThread].isMainThread?@"YES" :@"NO" );
});
NSLog (@"分组任务添加完毕 是否主线程:%@" ,[NSThread currentThread].isMainThread?@"YES" :@"NO" );
运行结果:
2017-04-06 15:38:26.260 GCD-Test[17391:8094544] 分组任务添加完毕 是否主线程:YES 2017-04-06 15:38:29.264 GCD-Test[17391:8094573] 任务1 是否主线程:NO 2017-04-06 15:38:29.264 GCD-Test[17391:8094577] 任务5 是否主线程:NO 2017-04-06 15:38:29.265 GCD-Test[17391:8094567] 任务2 是否主线程:NO 2017-04-06 15:38:29.266 GCD-Test[17391:8094572] 任务3 是否主线程:NO 2017-04-06 15:38:29.266 GCD-Test[17391:8094576] 任务4 是否主线程:NO 2017-04-06 15:38:29.266 GCD-Test[17391:8094576] thread: 0x13452ab30 是否主线程?:NO 任务全部执行完毕*
dispatch_group_async_f 和 dispatch_group_notify_f 的用法
先定义几个 C 函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void showStringForTask1 (void *obj) {
sleep(3 );
NSString *objString = (__bridge NSString *)obj;
NSLog (@"异步调度组任务1:%@ 是否主线程?:%@" ,objString,[NSThread currentThread].isMainThread?@"YES" :@"NO" );
}
void showStringForTask2 (void *obj) {
sleep(3 );
NSString *objString = (__bridge NSString *)obj;
NSLog (@"异步调度组任务2:%@ 是否主线程?:%@" ,objString,[NSThread currentThread].isMainThread?@"YES" :@"NO" );
}
void showStringForTask3 (void *obj) {
sleep(3 );
NSString *objString = (__bridge NSString *)obj;
NSLog (@"异步调度组任务3:%@ 是否主线程?:%@" ,objString,[NSThread currentThread].isMainThread?@"YES" :@"NO" );
}
void allTaskNofify(void *obj)
{
NSLog (@"分组任务添加完毕 是否主线程:%@" ,[NSThread currentThread].isMainThread?@"YES" :@"NO" );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
创建 并发队列talentQueue
参数 "TalentC.dispatch.queue.test" 队列的标记 可自定义
参数 DISPATCH_QUEUE_CONCURRENT 并发队列 (同时可选则 DISPATCH_QUEUE_SERIAL 串行队列)
返回 dispatch_queue_t 队列对象 dispatch_object
*/
dispatch_queue_t talentQueue = dispatch_queue_create("TalentC.dispatch.queue.test" , DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t talentGroup = dispatch_group_create();
创建任务1
@"我是参数" 参数可以使任意数据类型
showStringForTask1 任务函数 函数类型可以查看dispatch_function_t
*/
dispatch_group_async_f(talentGroup, talentQueue, @"我是参数" , showStringForTask1);
dispatch_group_async_f(talentGroup, talentQueue, @"我是一只小蜜蜂哈哈哈" , showStringForTask2);
dispatch_group_async_f(talentGroup, talentQueue, @"再来一个任务吧" , showStringForTask3);
dispatch_group_notify_f(talentGroup, talentQueue, nil , allTaskNofify);
NSLog (@"分组任务添加完毕 是否主线程:%@" ,[NSThread currentThread].isMainThread?@"YES" :@"NO" );
执行结果:
2017-04-06 16:04:50.116 GCD-Test[17428:8102843] 分组任务添加完毕 是否主线程:YES 2017-04-06 16:04:53.122 GCD-Test[17428:8102858] 异步调度组任务1:我是参数 是否主线程?:NO 2017-04-06 16:04:53.122 GCD-Test[17428:8102860] 异步调度组任务3:再来一个任务吧 是否主线程?:NO 2017-04-06 16:04:53.122 GCD-Test[17428:8102861] 异步调度组任务2:我是一只小蜜蜂哈哈哈 是否主线程?:NO 2017-04-06 16:04:53.123 GCD-Test[17428:8102861] 分组任务添加完毕 是否主线程:NO
2、等待群组任务完成disaptch_group_wait 1
long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
同步等待事先提交到群组中的任务完成。在指定的超时期限过去之前,返回这些block是否完成。当发生超时时,这个群组将恢复到原来的状态。
如果这个调度群组是空的(没有block与这个群组相关联),这个函数立即返回。 在这个函数成功返回之后,这个调度群组是空的。它既可以使用 dispatch_release 释放掉,也可以重新添加block。
成功(在指定的超时期限内,所有与群组相关联的block完成)将返回零。失败(超时发生)返回非零。
group,等待完成的调度群组。不可以为NULL。
timeout,超时时间(参考dispatch_time)。常量 DISPATCH_TIME_NOW (立即)和 DISPATCH_TIME_FOREVER (无穷大)被提供使用很方便。 示例: 我们将上面代码稍作修改子后面添加
long ret = dispatch_group_wait(talentGroup, DISPATCH_TIME_FOREVER);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dispatch_group_async_f(talentGroup, talentQueue, @"我是参数" , showStringForTask1);
dispatch_group_async_f(talentGroup, talentQueue, @"我是一只小蜜蜂哈哈哈" , showStringForTask2);
dispatch_group_async_f(talentGroup, talentQueue, @"再来一个任务吧" , showStringForTask3);
dispatch_group_notify_f(talentGroup, talentQueue, nil , allTaskNofify);
NSLog (@"分组任务添加完毕 是否主线程:%@" ,[NSThread currentThread].isMainThread?@"YES" :@"NO" );
long ret = dispatch_group_wait(talentGroup, DISPATCH_TIME_FOREVER);
if (ret == 0 ) {
NSLog (@"群组任务全部执行成功" );
}else {
NSLog (@"群组任务全部执行失败|超时" );
}
执行结果:
2017-04-06 16:22:28.435 GCD-Test[17433:8105482] 分组任务添加完毕 是否主线程:YES 2017-04-06 16:22:31.441 GCD-Test[17433:8105516] 异步调度组任务1:我是参数 是否主线程?:NO 2017-04-06 16:22:31.442 GCD-Test[17433:8105512] 异步调度组任务2:我是一只小蜜蜂哈哈哈 是否主线程?:NO 2017-04-06 16:22:31.442 GCD-Test[17433:8105510] 异步调度组任务3:再来一个任务吧 是否主线程?:NO 2017-04-06 16:22:31.442 GCD-Test[17433:8105482] 群组任务全部执行成功 2017-04-06 16:22:31.451 GCD-Test[17433:8105510] 分组任务添加完毕 是否主线程:NO
GCD Group的全部API及使用介绍完毕,个人可以按照实际需求自由组合。
Dispatch Apply dispatch_apply 文档注释解释说:Submits a block to a dispatch queue for multiple invocations. 提交一个block块到一个分发的队里,以供多次调用.
This function submits a block to a dispatch queue for multiple invocations and waits for all iterations of the task block to complete before returning. If the target queue is a concurrent queue returned by dispatch_get_global_queue, the block can be invoked concurrently, and it must therefore be reentrant-safe. Using this function with a concurrent queue can be useful as an efficient parallel for loop. The current index of iteration is passed to each invocation of the block. 摘自Apple 开发者文档
Apply 相关API 1
2
dispatch_apply(size_t iterations, dispatch_queue_t queue,DISPATCH_NOESCAPE void (^block)(size_t));
dispatch_apply_f(size_t iterations, dispatch_queue_t queue,void *_Nullable context,void (*work)(void *_Nullable, size_t));
一个任务执行5次 dispatch_apply 代码如下:1
2
3
4
dispatch_queue_t talentQueue = dispatch_queue_create("TalentC.dispatch.queue.test" , DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(5 , talentQueue, ^(size_t idx) {
NSLog (@"dispatch_apply 执行idx:%zu 是否主线程:%@" ,idx,[NSThread currentThread].isMainThread?@"YES" :@"NO" );
});
执行结果
2017-04-06 17:11:43.441 GCD-Test[17440:8111766] dispatch_apply 执行idx:0 是否主线程:YES 2017-04-06 17:11:43.441 GCD-Test[17440:8111786] dispatch_apply 执行idx:1 是否主线程:NO 2017-04-06 17:11:43.442 GCD-Test[17440:8111786] dispatch_apply 执行idx:3 是否主线程:NO 2017-04-06 17:11:43.442 GCD-Test[17440:8111786] dispatch_apply 执行idx:4 是否主线程:NO 2017-04-06 17:11:43.442 GCD-Test[17440:8111766] dispatch_apply 执行idx:2 是否主线程:YES
注意: 这里因为是并发线程所以不是按顺序输出,如果改成串行队列是可以按顺序输出的dispatch_apply_f(size_t iterations, dispatch_queue_t queue,void *_Nullable context,void (*work)(void *_Nullable, size_t));
用法这里就不解释了与上述 dispatch_group_async_f
用法相似 只是参数多了一个。
总结 这里讲解的都是一些基础用法对于初学者足以,通过自己动敲一遍代码会加深理解,建议初学者可以跟着敲一遍,遇到问题及时查资料,这样可以做到举一反三,要知其然知其所以然;下篇文章会介绍队列挂起/恢复、barrier(栅栏)等。