dispatch_once源码分析

GCD里的单例函数dispatch_once是我们经常会用到的,今天我们来稍做深入分析一下。GCD的源码都在libdispatch.dylib库里,这个库在libSystem_initializer被初始化,可理解为在dyld里被加载和初始化的(之前的文章有分析过)。dispatch_once作为单例的使用入口,通过分析得到它是一个宏定义,_dispatch_once函数在libdispatch.dylib可以查到。

dispatch_once宏
_dispatch_once函数

_dispatch_once函数会调用dispatch_once。

dispatch_once函数

最终来到dispatch_once_f,这里有实现单例函数的重要逻辑:

dispatch_once_f函数

我们来分析下这个函数,dispatch_once_t *val参数通常来自于外部传来的静态变量,静态变量的唯一性。静态值在函数内部被转换为l,通过获取l的dgo_once属性,判断为DLOCK_ONCE_DONE,如果是则返回。下面我们重点看下第67行代码,_dispatch_once_gate_tryenter函数。

_dispatch_once_gate_tryenter函数

_dispatch_once_gate_tryenter函数里,os_atomic_cmpxchg函数会对l->dgo_once和DLOCK_ONCE_UNLOCKED进行比较,看是否相等,如果不是就上锁_dispatch_lock_value_for_self,这样操作也能防止多线程访问的不安全性和唯一性。此时来到dispatch_once_f函数的第68行_dispatch_once_callout函数。

_dispatch_once_callout函数

这里的_dispatch_client_callout(ctxt, func)会对单例的block进行调用。接下来_dispatch_once_gate_broadcast(l)会对外进行“广播”,可理解为一种标识,意思就是已经执行了一次block,不能再执行了。

_dispatch_once_gate_broadcast函数

_dispatch_once_gate_broadcast函数里,(_dispatch_lock_value_for_self会进行解锁???)第670行_dispatch_once_mark_done函数。

_dispatch_once_mark_done函数

_dispatch_once_mark_done函数会把dgo_once设置成DLOCK_ONCE_DONE!!!我们可以回到dispatch_once_f函数第58行可知,再下一次访问的时候,这里就会return,也就是通过上锁达到了单例“执行一次”的效果。当多线程访问单例函数dispatch_once_f时候,会让其在70行_dispatch_once_wait进行等待,等到前面的线程访问结束,以保证线程安全。

PS:_dispatch_once_gate_tryenter函数进行加锁,_dispatch_once_gate_broadcast进行解锁。

你可能感兴趣的:(dispatch_once源码分析)