GCD的使用中存在两个特殊的队列,dispatch_get_main_queue()和 dispatch_get_global_queue(0, 0)。
dispatch_get_main_queue()是GCD自带的特殊的串行队列。所有放在主队列的任务都会在主线程执行,可使用dispatch_get_main_queue()获得主队列。下面回顾一下上文的思考题。
同步函数主队列
由图片可以看出,代码在打印出任务开始后,后面添加的任务都没有执行,而且报出了错误,产生了崩溃。我们可以在syncSerialTask的任务调用,是在主线程执行,添加到了主队列中,而同步执行会等待当前队列中的任务执行完毕,才会接着执行。那么当我们把任务一追加到主队列中,任务一就在等待主线程处理完syncSerialTask任务。而syncSerialTask任务需要等待任务1执行完毕,才能接着执行。这样就形成了互相等待,所以我们的任务无法执行。异步函数则不会有这种问题。
dispatch_get_global_queue来获取。需要传入两个参数。第一个参数表示队列优先级,一般用0。第二个参数暂时没有,GCD默认提供的全局并发队列。
下面我们看一下GCD的特殊函数。
GCD 栅栏方法:dispatch_barrier_async。
栅栏函数的使用通常使用在当我们需要在一系列任务完成后再执行其他的任务中使用,我们可以简单的理解为一个水坝,将一条河流分割。函数会等待先加入并发队列中的任务执行完毕后,再执行栅栏函数后的任务。
可以看出,会在栅栏函数执行完毕前面的的任务后再执行后续任务。
GCD方法:dispatch_once。
当我们的应用程序中有方法要求执行一次是进行使用,多用于单例等场景。
GCD延时方法:dispatch_after。
是在延迟一段时间后,执行对应的任务。dispatch_after函数并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after函数是很有效的。
GCD调度组 dispatch_group。
在开发项目中,我们可能会遇到一个页面的数据请求来自多个请求接口,要求我们在所有数据返回后,在进行页面数据的刷新的一个场景。这时候我们就需要使用调度组。调用队列组的 dispatch_group_async 先把任务放到队列中,然后将队列放入队列组中。或者使用队列组的 dispatch_group_enter、dispatch_group_leave组合 来实现dispatch_group_async。 调用队列组的 dispatch_group_notify 回到指定线程执行任务。或者使用 dispatch_group_wait 回到当前线程继续向下执行(会阻塞当前线程)。
dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数+1
dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数-1。
当 group 中未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞,以及执行追加到dispatch_group_notify中的任务。所以我们在任务中如果需要等待一些函数的回调,需要在回调函数中进行group的leave操作,否则后续任务不会执行。
GCD 信号量:dispatch_semaphore
信号娘是持有计数的信号。在dispatch_semaphore中,使用计数来完成这个功能,计数为0时等待,不可通过。计数为1或大于1时,计数减1且不等待,可通过。信号量提供了三个函数。dispatch_semaphore_create:创建一个Semaphore并初始化信号的总量,dispatch_semaphore_signal:发送一个信号,让信号总量加1,dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。信号量在项目中多用于线程安全和异步任务转换为同步任务。