dispatch_once (单例)
dispatch_once
函数是保证在应用程序执行中只执行一次指定处理的 API 。通过 dispatch_once
函数,该源代码即使在多线程环境下执行,也可保证百分之百安全。用 dispatch_once
函数初始化就不必担心重复初始化的问题。这就是所说的单例模式,在生成单例对象时使用。使用 dispatch_once函数
,则源代码写为:
static dispatch_once_t pred;
dispatch_once(&pred,^{
/*
*初始化
*/
}
dispatch_once 实现
void
dispatch_once(dispatch_once_t *val, dispatch_block_t block)
{
dispatch_once_f(val, block, _dispatch_Block_invoke(block));
}
dispatch_once_f
DISPATCH_NOINLINE
void
dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
dispatch_once_gate_t l = (dispatch_once_gate_t)val;
#if !DISPATCH_ONCE_INLINE_FASTPATH || DISPATCH_ONCE_USE_QUIESCENT_COUNTER
uintptr_t v = os_atomic_load(&l->dgo_once, acquire);
//如果已经执行了
if (likely(v == DLOCK_ONCE_DONE)) {
return;
}
#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
if (likely(DISPATCH_ONCE_IS_GEN(v))) {
return _dispatch_once_mark_done_if_quiesced(l, v);
}
#endif
#endif
//
if (_dispatch_once_gate_tryenter(l)) {
//进行调用
return _dispatch_once_callout(l, ctxt, func);
}
return _dispatch_once_wait(l);
}
dispatch_once_f
执行流程如下:
-
将
dispatch_once
,也就是静态变量转换为dispatch_once_gate_t
类型的变量 l; -
通过
os_atomic_load
获取此时的任务的标识符 v,如果 v 等于DLOCK_ONCE_DONE
,表示任务已经执行过了,直接返回 -
如果任务执行后,但是 v 不等于 DLOCK_ONCE_DONE,则走到 _dispatch_once_mark_done_if_quiesced 函数,再次进行存储,将标识符置为 DLOCK_ONCE_DONE
DISPATCH_ALWAYS_INLINE static inline void _dispatch_once_mark_done_if_quiesced(dispatch_once_gate_t dgo, uintptr_t gen) { if (_dispatch_once_generation() - gen >= DISPATCH_ONCE_GEN_SAFE_DELTA) { /* * See explanation above, when the quiescing counter approach is taken * then this store needs only to be relaxed as it is used as a witness * that the required barriers have happened. */ os_atomic_store(&dgo->dgo_once, DLOCK_ONCE_DONE, relaxed); } }
-
通过 _dispatch_once_gate_tryenter 尝试进入任务,即解锁。
DISPATCH_ALWAYS_INLINE static inline bool _dispatch_once_gate_tryenter(dispatch_once_gate_t l) { return os_atomic_cmpxchg(&l->dgo_once, DLOCK_ONCE_UNLOCKED, (uintptr_t)_dispatch_lock_value_for_self(), relaxed); }
-
调用
_dispatch_once_callout
,执行 block,并将 v = DLOCK_ONCE_DONEDISPATCH_NOINLINE static void _dispatch_once_callout(dispatch_once_gate_t l, void *ctxt, dispatch_function_t func) { //进行调用 _dispatch_client_callout(ctxt, func); //广播完成 _dispatch_once_gate_broadcast(l); } DISPATCH_ALWAYS_INLINE static inline void _dispatch_once_gate_broadcast(dispatch_once_gate_t l) { dispatch_lock value_self = _dispatch_lock_value_for_self(); uintptr_t v; #if DISPATCH_ONCE_USE_QUIESCENT_COUNTER v = _dispatch_once_mark_quiescing(l); #else //完成 v = _dispatch_once_mark_done(l); #endif if (likely((dispatch_lock)v == value_self)) return; _dispatch_gate_broadcast_slow(&l->dgo_gate, (dispatch_lock)v); }
-
如果此时有任务正在执行,则通过
_dispatch_once_wait
函数进入无限次等待,等待超时void _dispatch_once_wait(dispatch_once_gate_t dgo) { dispatch_lock self = _dispatch_lock_value_for_self(); uintptr_t old_v, new_v; #if HAVE_UL_UNFAIR_LOCK || HAVE_FUTEX dispatch_lock *lock = &dgo->dgo_gate.dgl_lock; #endif uint32_t timeout = 1; for (;;) { os_atomic_rmw_loop(&dgo->dgo_once, old_v, new_v, relaxed, { if (likely(old_v == DLOCK_ONCE_DONE)) { os_atomic_rmw_loop_give_up(return); } #if DISPATCH_ONCE_USE_QUIESCENT_COUNTER if (DISPATCH_ONCE_IS_GEN(old_v)) { os_atomic_rmw_loop_give_up({ os_atomic_thread_fence(acquire); return _dispatch_once_mark_done_if_quiesced(dgo, old_v); }); } #endif new_v = old_v | (uintptr_t)DLOCK_WAITERS_BIT; if (new_v == old_v) os_atomic_rmw_loop_give_up(break); }); if (unlikely(_dispatch_lock_is_locked_by((dispatch_lock)old_v, self))) { DISPATCH_CLIENT_CRASH(0, "trying to lock recursively"); } #if HAVE_UL_UNFAIR_LOCK _dispatch_unfair_lock_wait(lock, (dispatch_lock)new_v, 0, DLOCK_LOCK_NONE); #elif HAVE_FUTEX _dispatch_futex_wait(lock, (dispatch_lock)new_v, NULL, FUTEX_PRIVATE_FLAG); #else _dispatch_thread_switch(new_v, 0, timeout++); #endif (void)timeout; } }
总结
dispatch_once_t onceToken
是静态变量,具有唯一性,在底层被封装成了 dispatch_once_gate_t
类型的变量 l,l 主要是用来获取底层原子封装性的关联,即变量 v,通过 v 来查询任务的状态,如果此时 v 等于DLOCK_ONCE_DONE
,说明任务已经处理过一次了,执行时通过锁保证线程安全。
网友评论