作者Talent•C
转载请注明出处
前言
很抱歉,最近比较忙更新的有点晚了;前几篇文章我们介绍了GCD的概念、理论基础及基础使用,我们这篇文章主要介绍一下 dispatch_suspend
(队列挂起)、 dispatch_resume
(队列恢复)、 dispatch_barrier
(栅栏) 的使用。
suspend/resume
- suspend: 通过
dispatch_suspend()
函数实现队列的”挂起”,使队列暂停工作。但是这里的“挂起”,并不能立即停止队列上正在运行的block; - resume:
dispatch_resume()
函数恢复队列,是队列继续工作。
注意: dispatch_suspend 与 dispatch_resume 要成对出现。
原因:
我们可以使用dispatch_suspend函数暂停一个queue以阻止它执行block对象;使用dispatch_resume函数继续dispatch queue。调用dispatch_suspend会增加queue的引用计数,调用dispatch_resume则减少queue的引用计数。当计数大于0时,队列仍然被挂起,因此必须平衡每个dispatch_suspend调用与匹配的dispatch_resume调用。
摘自:Apple 开发者文档
现在我们来实现一个小功能:一个异步串行队列,向其中添加5任务,延迟7秒暂停这个队列,然后再延迟5秒回复队列.
|
|
输出结果:
2017-05-02 10:46:59.639 GCD-Test[77894:5941123] 任务添加完毕 是否主线程:YES
2017-05-02 10:46:59.639 GCD-Test[77894:5941325] 任务0开始执行 是否主线程:NO
2017-05-02 10:47:02.643 GCD-Test[77894:5941325] 任务0执行完毕 是否主线程:NO
2017-05-02 10:47:02.644 GCD-Test[77894:5941325] 任务1开始执行 是否主线程:NO
2017-05-02 10:47:05.644 GCD-Test[77894:5941325] 任务1执行完毕 是否主线程:NO
2017-05-02 10:47:05.644 GCD-Test[77894:5941325] 任务2开始执行 是否主线程:NO
2017-05-02 10:47:06.640 GCD-Test[77894:5941123] 队列延迟7秒挂起 是否主线程:YES
2017-05-02 10:47:08.649 GCD-Test[77894:5941325] 任务2执行完毕 是否主线程:NO
2017-05-02 10:47:11.640 GCD-Test[77894:5941123] 队列挂起后延迟5秒恢复 是否主线程:YES
2017-05-02 10:47:11.652 GCD-Test[77894:5941325] 任务3开始执行 是否主线程:NO
2017-05-02 10:47:14.664 GCD-Test[77894:5941325] 任务3执行完毕 是否主线程:NO
2017-05-02 10:47:14.664 GCD-Test[77894:5941325] 任务4开始执行 是否主线程:NO
2017-05-02 10:47:17.667 GCD-Test[77894:5941325] 任务4执行完毕 是否主线程:NO
通过打印结果我们可以验证当调用 dispatch_suspend(talentQueue)
“挂起”队列 talentQueue
后已经开始执行的任务不会挂起,而未开始的任务可以挂起。
dispatch_barrier
官方解释:
一个 dispatch barrier 允许在一个并发队列中创建一个同步点。当在并发队列中遇到一个 barrier, 他会延迟执行 barrier 的 block ,等待所有在 barrier 之前提交的 blocks 执行结束。 这时,barrier block 自己开始执行。 之后, 队列继续正常的执行操作。
调用这个函数总是在 barrier block 被提交之后立即返回,不会等到 block被执行。当 barrier block 到并发队列的最前端,他不会立即执行。相反,队列会等到所有当前正在执行的 blocks结束执行。到这时,barrier 才开始自己执行。所有在 barrier block 之后提交的 blocks 会等到 barrier block 结束之后才执行。
这里指定的并发队列应该是自己通过 dispatch_queue_create 函数创建的。如果你传的是一个串行队列或者全局并发队列,这个函数等同于dispatch_async函数。
摘自:Apple 开发者文档
相关API:
dispatch_barrier_sync
与 dispatch_barrier_async
的区别
相同点:
- 等待在它前面插入队列的任务先执行完。
- 等待他们自己的任务执行完再执行后面的任务。
不同点: - dispatch_barrier_sync将自己的任务插入到队列的时候,需要等待自己的任务结束之后才会继续插入被写在它后面的任务,然后执行它们。
- dispatch_barrier_async将自己的任务插入到队列之后,不会等待自己的任务结束,它会继续把后面的任务插入到队列,然后等待自己的任务结束后才执行后面任务。
代码示例:123456789101112131415161718192021222324252627282930313233343536dispatch_queue_t talentQueue_bar = dispatch_queue_create("talentQueue.barrier.test", DISPATCH_QUEUE_CONCURRENT);for (int i = 0; i < 5; i ++) {if (i == 2) {dispatch_barrier_async(talentQueue_bar, ^{NSString *taskString = [NSString stringWithFormat:@"任务%d barrier任务",i];NSLog(@"%@开始执行 是否主线程:%@",taskString,[NSThread currentThread].isMainThread?@"YES":@"NO");//这里使用 GCD 的计时器代替 sleep(3) 可以更加明显的看出效果__block int count_t = 0;dispatch_queue_t queue = dispatch_get_main_queue();dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));uint64_t interval = (uint64_t)(0.5 * NSEC_PER_SEC);dispatch_source_set_timer(timer, start, interval, 0);dispatch_source_set_event_handler(timer, ^{CGFloat p = count_t/10.0;p = p>1?1:p;if (p == 1) {//取消定时器dispatch_cancel(timer);NSLog(@"%@执行完毕 是否主线程:%@",taskString,[NSThread currentThread].isMainThread?@"YES":@"NO");}count_t++;});// 启动定时器dispatch_resume(timer);});}else {dispatch_async(talentQueue_bar, ^{NSString *taskString = [NSString stringWithFormat:@"任务%d",i];NSLog(@"%@开始执行 是否主线程:%@",taskString,[NSThread currentThread].isMainThread?@"YES":@"NO");sleep(3);NSLog(@"%@执行完毕 是否主线程:%@",taskString,[NSThread currentThread].isMainThread?@"YES":@"NO");});}}NSLog(@"任务添加完毕 是否主线程:%@",[NSThread currentThread].isMainThread?@"YES":@"NO");
结果打印:
2017-05-02 12:11:11.232 GCD-Test[80817:5993383] 任务添加完毕 是否主线程:YES
2017-05-02 12:11:11.232 GCD-Test[80817:5993420] 任务0开始执行 是否主线程:NO
2017-05-02 12:11:11.232 GCD-Test[80817:5993422] 任务1开始执行 是否主线程:NO
2017-05-02 12:11:14.233 GCD-Test[80817:5993420] 任务0执行完毕 是否主线程:NO
2017-05-02 12:11:14.237 GCD-Test[80817:5993422] 任务1执行完毕 是否主线程:NO
2017-05-02 12:11:14.237 GCD-Test[80817:5993422] 任务2 barrier任务开始执行 是否主线程:NO
2017-05-02 12:11:14.238 GCD-Test[80817:5993422] 任务3开始执行 是否主线程:NO
2017-05-02 12:11:14.238 GCD-Test[80817:5993420] 任务4开始执行 是否主线程:NO
2017-05-02 12:11:17.238 GCD-Test[80817:5993420] 任务4执行完毕 是否主线程:NO
2017-05-02 12:11:17.238 GCD-Test[80817:5993422] 任务3执行完毕 是否主线程:NO
2017-05-02 12:11:20.238 GCD-Test[80817:5993383] 任务2 barrier任务执行完毕 是否主线程:YES
从打印结果可以看出来等待任务0 和 任务1 执行完毕后开始执行 barrier任务 。但是因为这里用但是dispatch_barrier_async
是异步的,所以不会等待 barrier任务 执行完毕再开启下一个任务,如果希望barrier任务 执行完毕后再开始其他任务则使用dispatch_barrier_sync
。
barrier 相关API
总结
我们在使用dispatch_suspend
和dispatch_resume
要注意他们两个要成对出现以避免队列不执行,永远挂起的情况;在使用dispatch_barrier
时要注意dispatch_barrier
的函数特性注意加入队列的位置及时机。