iOS 底层探索篇 —— GCD栅栏函数、信号量、调度组和Dispatch_Source
1. 栅栏函数
1.1 栅栏函数的作用
栅栏函数
最直接的作用是控制任务执行顺序,同步
。
dispatch_barrier_async
前面的任务执行完毕才会来到这里dispatch_barrier_sync
作用相同,但是这个会堵塞线程,影响后面的任务执行- 栅栏函数在
全局队列中是无效的
- 非常重要的一点: 栅栏函数只能控制
同一并发队列
这里的函数运行状况是,123
, 456
和起来干
是随机打印的,而dispatch_barrier_async里面的函数
和加载那么多喘口气
是一定在456后面
运行的。
如果是dispatch_barrier_sync
,那么就会堵塞线程
,影响后面的任务执行,也就是说会影响起来干
的执行和加载那么多
的执行,这两个任务会在栅栏函数执行完后执行,执行顺序不固定。
如果栅栏函数和其他函数不在同一队列,那么就不会影响其他队列任务的执行
。
如果是全局队列的话,那么栅栏函数也是无效的
。
栅栏函数还能起到锁
的作用。
数组是线程不安全
的,这样异步向数组添加元素的话是会崩溃的。在多线程条件下,mArray的地址永远是同一个
,在执行过程中,mArray元素的个数不停的变化。在添加元素的时候,需要对mArray进行一个读的操作,然后在进行一个写的操作
。而写的操作就会有对新值的retain和对旧值的release
。这个时候如果有一个多线程进行同时对一个位置写的操作,也就是对同一时间对同一内存空间进行操作
,那么就会不安全就会报出异常。
但是如果加入栅栏函数的话,就不会崩溃了。
1.2 栅栏函数的原理
栅栏函数是如何实现的呢?
看到dispatch_barrier_sync
会调用_dispatch_barrier_sync_f
。
接着跳转_dispatch_barrier_sync_f_inline
。
这里来到_dispatch_barrier_sync_f_inline
。
然后看到_dispatch_sync_recurse
。这里进行死循环递归,确保之前的任务已经清空了,然后进行自己block的调用.
接着看_dispatch_sync_invoke_and_complete_recurse
。
然后来到_dispatch_sync_complete_recurse
。这里判断是否存在barrier
,如果存在,就调用dx_wake
将队列的任务进行唤起执行,然后才调用_dispatch_lane_non_barrier_complete
进行状态的修改。
查找一下dx_wake 也就是dq_wake。普通的队列是_dispatch_lane_wakeup
,全局队列则是_dispatch_root_queue_wakeup
。_dispatch_lane_wakeup中如果是barrier形式,就会调用_dispatch_lane_barrier_complete
,否则就调用_dispatch_queue_wakeup
。而全局队列则没有对barrier进行处理,也就是说barrier对全局队列没有影响
。全局队列对栅栏函数不处理的原因是,全局队列不仅仅被我们使用,也被系统使用。如果对栅栏函数进行处理,那么可能影响到系统任务的执行,
所以全局队列不对barrier进行处理。