GCD ④ dispatch_once

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 执行流程如下:

  1. dispatch_once,也就是静态变量转换为 dispatch_once_gate_t类型的变量 l;

  2. 通过 os_atomic_load 获取此时的任务的标识符 v,如果 v 等于 DLOCK_ONCE_DONE,表示任务已经执行过了,直接返回

  3. 如果任务执行后,但是 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);
     }
    }
    
  4. 通过 _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);
    }
    
    
  5. 调用 _dispatch_once_callout,执行 block,并将 v = DLOCK_ONCE_DONE

    DISPATCH_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);
    }
    
    
  6. 如果此时有任务正在执行,则通过 _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,说明任务已经处理过一次了,执行时通过锁保证线程安全。

你可能感兴趣的:(GCD ④ dispatch_once)