dispatch_once (單例)
????dispatch_once
函數(shù)是保證在應(yīng)用程序執(zhí)行中只執(zhí)行一次指定處理的 API 。通過 dispatch_once
函數(shù)坚俗,該源代碼即使在多線程環(huán)境下執(zhí)行镜盯,也可保證百分之百安全岸裙。用 dispatch_once
函數(shù)初始化就不必?fù)?dān)心重復(fù)初始化的問題。這就是所說的單例模式速缆,在生成單例對象時(shí)使用哥桥。使用 dispatch_once函數(shù)
,則源代碼寫為:
static dispatch_once_t pred;
dispatch_once(&pred,^{
/*
*初始化
*/
}
dispatch_once 實(shí)現(xiàn)
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);
//如果已經(jīng)執(zhí)行了
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)) {
//進(jìn)行調(diào)用
return _dispatch_once_callout(l, ctxt, func);
}
return _dispatch_once_wait(l);
}
dispatch_once_f
執(zhí)行流程如下:
將
dispatch_once
激涤,也就是靜態(tài)變量轉(zhuǎn)換為dispatch_once_gate_t
類型的變量 l;通過
os_atomic_load
獲取此時(shí)的任務(wù)的標(biāo)識符 v,如果 v 等于DLOCK_ONCE_DONE
判呕,表示任務(wù)已經(jīng)執(zhí)行過了倦踢,直接返回-
如果任務(wù)執(zhí)行后,但是 v 不等于 DLOCK_ONCE_DONE侠草,則走到 _dispatch_once_mark_done_if_quiesced 函數(shù)辱挥,再次進(jìn)行存儲,將標(biāo)識符置為 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 嘗試進(jìn)入任務(wù)边涕,即解鎖晤碘。
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); }
-
調(diào)用
_dispatch_once_callout
,執(zhí)行 block功蜓,并將 v = DLOCK_ONCE_DONEDISPATCH_NOINLINE static void _dispatch_once_callout(dispatch_once_gate_t l, void *ctxt, dispatch_function_t func) { //進(jìn)行調(diào)用 _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); }
-
如果此時(shí)有任務(wù)正在執(zhí)行园爷,則通過
_dispatch_once_wait
函數(shù)進(jìn)入無限次等待,等待超時(shí)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; } }
總結(jié)
???? dispatch_once_t onceToken
是靜態(tài)變量式撼,具有唯一性童社,在底層被封裝成了 dispatch_once_gate_t
類型的變量 l,l 主要是用來獲取底層原子封裝性的關(guān)聯(lián)著隆,即變量 v扰楼,通過 v 來查詢?nèi)蝿?wù)的狀態(tài),如果此時(shí) v 等于DLOCK_ONCE_DONE
美浦,說明任務(wù)已經(jīng)處理過一次了弦赖,執(zhí)行時(shí)通過鎖保證線程安全。