OC底層原理20-GCD底層分析

iOS--OC底層原理文章匯總

前一文章介紹了GCD的概念寞蚌、使用林螃,以及函數(shù)與隊(duì)列搭配情況纽竣,本文將就GCD底層進(jìn)行分析坠韩。

隊(duì)列創(chuàng)建

準(zhǔn)備libdispatch工程卑笨。(進(jìn)入Apple的工程下載找到libdispatch

分析隊(duì)列創(chuàng)建就不得不在底層源碼中分析dispatch_queue_create孕暇,我們查詢?cè)创a工程找到了以下定義:

dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
{
        /**
         label: 是隊(duì)列標(biāo)簽,比如自定義的 com.myQueue,系統(tǒng)的 com.apple.main-thread
        */
    return _dispatch_lane_create_with_target(label, attr,
            DISPATCH_TARGET_QUEUE_DEFAULT, true);
}
DISPATCH_NOINLINE
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
        dispatch_queue_t tq, bool legacy)
{
    // 創(chuàng)建 dqai  可以獲得是否為并發(fā) 或 串行隊(duì)列
    dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

    //
    // Step 1: Normalize arguments (qos, overcommit, tq)
    //  規(guī)范化參數(shù): qos赤兴,overcommit妖滔,tq

    dispatch_qos_t qos = dqai.dqai_qos;
#if !HAVE_PTHREAD_WORKQUEUE_QOS
    if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
        dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
    }
    if (qos == DISPATCH_QOS_MAINTENANCE) {
        dqai.dqai_qos = qos = DISPATCH_QOS_BACKGROUND;
    }
#endif // !HAVE_PTHREAD_WORKQUEUE_QOS

    _dispatch_queue_attr_overcommit_t overcommit = dqai.dqai_overcommit;
    if (overcommit != _dispatch_queue_attr_overcommit_unspecified && tq) {
        if (tq->do_targetq) {
            DISPATCH_CLIENT_CRASH(tq, "Cannot specify both overcommit and "
                    "a non-global target queue");
        }
    }
    //.........
        
    //
    // Step 2: Initialize the queue 初始化queue
    //
    const void *vtable;
    dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
    if (dqai.dqai_concurrent) {
        // 獲取拼接類信息 OS_dispatch_queue_concurrent
        vtable = DISPATCH_VTABLE(queue_concurrent);
    } else {
       // OS_dispatch_queue_serial
        vtable = DISPATCH_VTABLE(queue_serial);
    }
    switch (dqai.dqai_autorelease_frequency) {
    case DISPATCH_AUTORELEASE_FREQUENCY_NEVER:
        dqf |= DQF_AUTORELEASE_NEVER;
        break;
    case DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM:
        dqf |= DQF_AUTORELEASE_ALWAYS;
        break;
    }
    if (label) {
        const char *tmp = _dispatch_strdup_if_mutable(label);
        if (tmp != label) {
            dqf |= DQF_LABEL_NEEDS_FREE;
            label = tmp;
        }
    }
    // 創(chuàng)建隊(duì)列
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s));
    // 初始化隊(duì)列;根據(jù)dqai.dqai_concurrent的值桶良,決定隊(duì)列 是 串行 還是并發(fā)
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));
         //.........
        // 設(shè)置隊(duì)列標(biāo)識(shí)符
    dq->dq_label = label;
    dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,
            dqai.dqai_relpri);
    if (overcommit == _dispatch_queue_attr_overcommit_enabled) {
        dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
    }
    if (!dqai.dqai_inactive) {
        _dispatch_queue_priority_inherit_from_target(dq, tq);
        _dispatch_lane_inherit_wlh_from_target(dq, tq);
    }
    _dispatch_retain(tq);
    dq->do_targetq = tq;
    _dispatch_object_debug(dq, "%s", __func__);
    //   dp作為研究對(duì)象
    return _dispatch_trace_queue_create(dq)._dq;
}

_dispatch_trace_queue_create -> _dispatch_introspection_queue_create(dqu);
_dispatch_introspection_queue_create對(duì)上一層創(chuàng)建之后的dq經(jīng)過兩層包裝座舍、處理再返回了dq

_dispatch_introspection_queue_create(dispatch_queue_t dq)
{
    dispatch_queue_introspection_context_t dqic;
    size_t sz = sizeof(struct dispatch_queue_introspection_context_s);

    if (!_dispatch_introspection.debug_queue_inversions) {
        sz = offsetof(struct dispatch_queue_introspection_context_s,
                __dqic_no_queue_inversion);
    }
    dqic = _dispatch_calloc(1, sz);
    dqic->dqic_queue._dq = dq;
    if (_dispatch_introspection.debug_queue_inversions) {
        LIST_INIT(&dqic->dqic_order_top_head);
        LIST_INIT(&dqic->dqic_order_bottom_head);
    }
    dq->do_finalizer = dqic;

    _dispatch_unfair_lock_lock(&_dispatch_introspection.queues_lock);
    LIST_INSERT_HEAD(&_dispatch_introspection.queues, dqic, dqic_list);
    _dispatch_unfair_lock_unlock(&_dispatch_introspection.queues_lock);

    DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_create, dq);
    if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_create)) {
        _dispatch_introspection_queue_create_hook(dq);
    }
    return upcast(dq)._dqu;
}

需要研究_dispatch_lane_create_with_target中做了哪些操作陨帆,都是圍繞dq展開曲秉。

  • 1、_dispatch_queue_attr_to_info創(chuàng)建類型對(duì)象疲牵,用來存儲(chǔ)隊(duì)列的相關(guān)信息承二。

  • 2、設(shè)置隊(duì)列的關(guān)聯(lián)屬性qos

  • 3纲爸、初始化隊(duì)列亥鸠;通過DISPATCH_VTABLE拼接隊(duì)列名稱

#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)
#define DISPATCH_OBJC_CLASS(name)   (&DISPATCH_CLASS_SYMBOL(name))
#define DISPATCH_CLASS_SYMBOL(name) OS_dispatch_##name##_class
#define DISPATCH_CLASS(name) OS_dispatch_##name

判斷dqai.dqai_concurrent決定是串行隊(duì)列還是并發(fā)隊(duì)列。

并發(fā)隊(duì)列:OS_dispatch_queue_serial
并發(fā)隊(duì)列:OS_dispatch_queue_concurrent

  • 4识啦、_dispatch_object_alloc創(chuàng)建隊(duì)列
void *
_dispatch_object_alloc(const void *vtable, size_t size)
{
#if OS_OBJECT_HAVE_OBJC1
    const struct dispatch_object_vtable_s *_vtable = vtable;
    dispatch_object_t dou;
    dou._os_obj = _os_object_alloc_realized(_vtable->_os_obj_objc_isa, size);
    dou._do->do_vtable = vtable;
    return dou._do;
#else
    return _os_object_alloc_realized(vtable, size);
#endif
}
??
inline _os_object_t
_os_object_alloc_realized(const void *cls, size_t size)
{
    _os_object_t obj;
    dispatch_assert(size >= sizeof(struct _os_object_s));
    while (unlikely(!(obj = calloc(1u, size)))) {
        _dispatch_temporary_resource_shortage();
    }
    obj->os_obj_isa = cls;  // 更改isa指向
    return obj;
}

創(chuàng)建隊(duì)列過程中负蚊,有更改isa指向操作,說明隊(duì)列是對(duì)象颓哮。

  • 5家妆、_dispatch_queue_init初始化dq,設(shè)置一些屬性
// Note to later developers: ensure that any initialization changes are
// made for statically allocated queues (i.e. _dispatch_main_q).
static inline dispatch_queue_class_t
_dispatch_queue_init(dispatch_queue_class_t dqu, dispatch_queue_flags_t dqf,
        uint16_t width, uint64_t initial_state_bits)
{
    uint64_t dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(width);
    dispatch_queue_t dq = dqu._dq;

    dispatch_assert((initial_state_bits & ~(DISPATCH_QUEUE_ROLE_MASK |
            DISPATCH_QUEUE_INACTIVE)) == 0);

    if (initial_state_bits & DISPATCH_QUEUE_INACTIVE) {
        dq->do_ref_cnt += 2; // rdar://8181908 see _dispatch_lane_resume
        if (dx_metatype(dq) == _DISPATCH_SOURCE_TYPE) {
            dq->do_ref_cnt++; // released when DSF_DELETED is set
        }
    }

    dq_state |= initial_state_bits;
    dq->do_next = DISPATCH_OBJECT_LISTLESS;
    dqf |= DQF_WIDTH(width);
    os_atomic_store2o(dq, dq_atomic_flags, dqf, relaxed);
    dq->dq_state = dq_state;
    dq->dq_serialnum =
            os_atomic_inc_orig(&_dispatch_queue_serial_numbers, relaxed);
    return dqu;
}

初始化完成之后設(shè)置dq_label、dq_priority

  • 6题翻、_dispatch_trace_queue_create -> _dispatch_introspection_queue_create -> return upcast(dq)._dqu; ,通過對(duì)隊(duì)列進(jìn)行操作并將處理完的dq返回揩徊。

全局隊(duì)列

dispatch_queue_global_t
dispatch_get_global_queue(long priority, unsigned long flags)
{
    dispatch_assert(countof(_dispatch_root_queues) ==
            DISPATCH_ROOT_QUEUE_COUNT);

    if (flags & ~(unsigned long)DISPATCH_QUEUE_OVERCOMMIT) {
        return DISPATCH_BAD_INPUT;
    }
    dispatch_qos_t qos = _dispatch_qos_from_queue_priority(priority);
#if !HAVE_PTHREAD_WORKQUEUE_QOS
    if (qos == QOS_CLASS_MAINTENANCE) {
        qos = DISPATCH_QOS_BACKGROUND;
    } else if (qos == QOS_CLASS_USER_INTERACTIVE) {
        qos = DISPATCH_QOS_USER_INITIATED;
    }
#endif
    if (qos == DISPATCH_QOS_UNSPECIFIED) {
        return DISPATCH_BAD_INPUT;
    }
    return _dispatch_get_root_queue(qos, flags & DISPATCH_QUEUE_OVERCOMMIT);
}

// ----------------------

DISPATCH_ALWAYS_INLINE DISPATCH_CONST
static inline dispatch_queue_global_t
_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
{
    if (unlikely(qos < DISPATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) {
        DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");
    }
    return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}

從底層獲取獲取合適的隊(duì)列,這可以佐證它的并發(fā)性嵌赠。
通過dispatch_queue_global_s這個(gè)結(jié)構(gòu)體獲取全局各種屬性的global_quue.

獲取底層全局隊(duì)列

4個(gè)隊(duì)列

主隊(duì)列

/*!
 * @function dispatch_get_main_queue
 *
 * @abstract
 * Returns the default queue that is bound to the main thread.
 *
 * @discussion
 * In order to invoke blocks submitted to the main queue, the application must
 * call dispatch_main(), NSApplicationMain(), or use a CFRunLoop on the main
 * thread.
 *
 * The main queue is meant to be used in application context to interact with  the main thread and the main runloop.
 *
 * Because the main queue doesn't behave entirely like a regular serial queue,
 * it may have unwanted side-effects when used in processes that are not UI apps
 * (daemons). For such processes, the main queue should be avoided.
 *
 * @see dispatch_queue_main_t
 *
 * @result
 * Returns the main queue. This queue is created automatically on behalf of the main thread before main() is called.
 */
DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_CONST DISPATCH_NOTHROW
dispatch_queue_main_t
dispatch_get_main_queue(void)
{
    return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);
}

可以得到幾點(diǎn)信息:
1.它是綁定到主線程的默認(rèn)隊(duì)列;
2.主隊(duì)列用于在應(yīng)用程序上下文中與主線程和主運(yùn)行循環(huán)進(jìn)行交互
3.主隊(duì)列不想常規(guī)的串行隊(duì)列熄赡,在非UI應(yīng)用程序使用姜挺,反而會(huì)發(fā)生不好的作用,應(yīng)該避免
4.它是自動(dòng)創(chuàng)建的隊(duì)列彼硫,且發(fā)生在main()調(diào)用之前炊豪。

通過查找dispatch_queue_main_t凌箕,我們找到了它的定義,它是綁定到主線程的默認(rèn)隊(duì)列的類型词渤。也指出了主隊(duì)列是一個(gè)串行隊(duì)列牵舱。主隊(duì)列是一個(gè)眾所周知的全局對(duì)象,該對(duì)象自動(dòng)在在進(jìn)程初始化期間代表主線程缺虐,并由返回dispatch_get_main_queue()芜壁,并且無法修改該對(duì)象。

dispatch_queue_main_t定義

由上面的信息:創(chuàng)建隊(duì)列發(fā)生在mian()調(diào)用之前高氮,這可以聯(lián)想到dyld的流程慧妄,在OC底層原理10—應(yīng)用程序加載中流程圖中簡(jiǎn)單介紹到main調(diào)用之前,初始化時(shí)會(huì)走底層的libdispatch_init.

libdispatch_init關(guān)于主隊(duì)列的創(chuàng)建

函數(shù):異步函數(shù) & 同步函數(shù)

異步函數(shù)

先通過打印異步函數(shù)的堆棧來看看異步函數(shù)調(diào)用過程中底層的調(diào)用順序


異步函數(shù)調(diào)用的堆棧順序

_dispatch_root_queue_drain -> _dispatch_continuation_pop_inline -> _dispatch_continuation_invoke_inline -> _dispatch_client_callout -> _dispatch_call_block_and_release

DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_continuation_invoke_inline(dispatch_object_t dou,
        dispatch_invoke_flags_t flags, dispatch_queue_class_t dqu)
{
    dispatch_continuation_t dc = dou._dc, dc1;
    dispatch_invoke_with_autoreleasepool(flags, {
        uintptr_t dc_flags = dc->dc_flags;
        // Add the item back to the cache before calling the function. This
        // allows the 'hot' continuation to be used for a quick callback.
        //
        // The ccache version is per-thread.
        // Therefore, the object has not been reused yet.
        // This generates better assembly.
        _dispatch_continuation_voucher_adopt(dc, dc_flags);
        if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {
            _dispatch_trace_item_pop(dqu, dou);
        }
        if (dc_flags & DC_FLAG_CONSUME) {
            dc1 = _dispatch_continuation_free_cacheonly(dc);
        } else {
            dc1 = NULL;
        }
        if (unlikely(dc_flags & DC_FLAG_GROUP_ASYNC)) {
            _dispatch_continuation_with_group_invoke(dc);
        } else {
      // 里面為f(ctxt)返回 ,實(shí)際就是  _dispatch_call_block_and_release
            _dispatch_client_callout(dc->dc_ctxt, dc->dc_func);
  //
            _dispatch_trace_item_complete(dc);
        }
        if (unlikely(dc1)) {
            _dispatch_continuation_free_to_cache_limit(dc1);
        }
    });
    _dispatch_perfmon_workitem_inc();
}

// ---------
#undef _dispatch_client_callout
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
    @try {
        return f(ctxt); // 返回一個(gè)方法調(diào)用,實(shí)際為  _dispatch_call_block_and_release
    }
    @catch (...) {
        objc_terminate();
    }
}

有了堆棧調(diào)用順序的初步了解剪芍,再探究dispatch_async源碼塞淹,如下:

void
dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
{
    // 創(chuàng)建一個(gè)持續(xù)調(diào)度的對(duì)象
    dispatch_continuation_t dc = _dispatch_continuation_alloc();
    uintptr_t dc_flags = DC_FLAG_CONSUME;
    dispatch_qos_t qos;
    /**
     初始化調(diào)度對(duì)象:包裝任務(wù)->接收work,保存work
    */
    qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
    // 并發(fā)處理
    _dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}
// --------------
DISPATCH_ALWAYS_INLINE
static inline dispatch_continuation_t
_dispatch_continuation_alloc(void)
{
    dispatch_continuation_t dc =
            _dispatch_continuation_alloc_cacheonly();
    if (unlikely(!dc)) {
        return _dispatch_continuation_alloc_from_heap();
    }
    return dc;
}
  • _dispatch_continuation_init :任務(wù)包裝器
DISPATCH_ALWAYS_INLINE
static inline dispatch_qos_t
_dispatch_continuation_init(dispatch_continuation_t dc,
        dispatch_queue_class_t dqu, dispatch_block_t work,
        dispatch_block_flags_t flags, uintptr_t dc_flags)
{
    // 拷貝任務(wù)
    void *ctxt = _dispatch_Block_copy(work);

    dc_flags |= DC_FLAG_BLOCK | DC_FLAG_ALLOCATED;
    if (unlikely(_dispatch_block_has_private_data(work))) {
   
        dc->dc_flags = dc_flags;
        dc->dc_ctxt = ctxt; // 將拷貝的任務(wù)地址賦值
        // 將初始化所有字段罪裹,但需要設(shè)置dc_flags和dc_ctxt
        return _dispatch_continuation_init_slow(dc, dqu, flags);
    }
     //封裝work  -> 同步回調(diào)函數(shù)
    dispatch_function_t func = _dispatch_Block_invoke(work);
    if (dc_flags & DC_FLAG_CONSUME) {
    //異步回調(diào)函數(shù)
        func = _dispatch_call_block_and_release;
    }
    // 給回調(diào)函數(shù)賦值
    return _dispatch_continuation_init_f(dc, dqu, ctxt, func, flags, dc_flags);
}

// 0----------0
#define _dispatch_Block_invoke(bb) \
        ((dispatch_function_t)((struct Block_layout *)bb)->invoke)

//0---------0
void
_dispatch_call_block_and_release(void *block)
{
    void (^b)(void) = block;
    b();
    Block_release(b);
}

小結(jié):_dispatch_continuation_init包裝任務(wù)饱普,并設(shè)置線程的回調(diào)函數(shù),初始化調(diào)度對(duì)象状共,保存在qos的屬性中套耕。

  • _dispatch_continuation_async
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_continuation_async(dispatch_queue_class_t dqu,
        dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)
{
#if DISPATCH_INTROSPECTION
    if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {
        _dispatch_trace_item_push(dqu, dc);
    }
#else
    (void)dc_flags;
#endif
    return dx_push(dqu._dq, dc, qos);
}

并發(fā)執(zhí)行block,其中dx_push(dqu._dq, dc, qos)定義如下

#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)

dq_push則根據(jù)傳入的隊(duì)列類型口芍,分別執(zhí)行以下等不同的函數(shù)

dq_push根據(jù)隊(duì)列執(zhí)行對(duì)應(yīng)函數(shù)

總結(jié):異步函數(shù)被調(diào)用時(shí)箍铲,會(huì)先創(chuàng)建一個(gè)持續(xù)調(diào)度隊(duì)列的對(duì)象dc,然后初始化調(diào)度對(duì)象鬓椭,并將待執(zhí)行任務(wù)包裝颠猴,pthread開辟線程再通過invoke執(zhí)行回調(diào)block,并發(fā)執(zhí)行不同的調(diào)度函數(shù)是由隊(duì)列的類型決定小染。

同步函數(shù)

dispatch_sync 源碼如下

DISPATCH_NOINLINE
void
dispatch_sync(dispatch_queue_t dq, dispatch_block_t work)
{
    uintptr_t dc_flags = DC_FLAG_BLOCK;
    if (unlikely(_dispatch_block_has_private_data(work))) {
        return _dispatch_sync_block_with_privdata(dq, work, dc_flags);
    }
    _dispatch_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags);
}
  • _dispatch_sync_f 流程
DISPATCH_NOINLINE
static void
_dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func,
        uintptr_t dc_flags)
{
    _dispatch_sync_f_inline(dq, ctxt, func, dc_flags);
}
??
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_sync_f_inline(dispatch_queue_t dq, void *ctxt,
        dispatch_function_t func, uintptr_t dc_flags)
{
    if (likely(dq->dq_width == 1)) {
      // 判斷 dq_width 是否為1翘瓮,表示一次只執(zhí)行一個(gè)。就返回柵欄函數(shù)的同步實(shí)現(xiàn)
        return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags);
    }
    // 檢查隊(duì)列的類型是否正確 dispatch_sync
    if (unlikely(dx_metatype(dq) != _DISPATCH_LANE_TYPE)) {
        DISPATCH_CLIENT_CRASH(0, "Queue type doesn't support dispatch_sync");
    }

    dispatch_lane_t dl = upcast(dq)._dl;
    // Global concurrent queues and queues bound to non-dispatch threads
    // always fall into the slow case, see DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE
    // 如果為全局并發(fā)隊(duì)列或者綁定到非調(diào)度線程的隊(duì)列裤翩,就進(jìn)行緩慢同步執(zhí)行资盅,其實(shí)做一些判斷之后最終還是要挨個(gè)執(zhí)行
    if (unlikely(!_dispatch_queue_try_reserve_sync_width(dl))) {
        return _dispatch_sync_f_slow(dl, ctxt, func, 0, dl, dc_flags);
    }

    if (unlikely(dq->do_targetq->do_targetq)) {
    // 遞歸同步執(zhí)行,一個(gè)一個(gè)執(zhí)行踊赠,直到全部執(zhí)行完成
        return _dispatch_sync_recurse(dl, ctxt, func, dc_flags);
    }
    // 內(nèi)部開始同步執(zhí)行
    _dispatch_introspection_sync_begin(dl);
    // 執(zhí)行完呵扛,執(zhí)行block回調(diào)
    _dispatch_sync_invoke_and_complete(dl, ctxt, func DISPATCH_TRACE_ARG(
            _dispatch_trace_item_sync_push_pop(dq, ctxt, func, dc_flags)));
}
??
DISPATCH_NOINLINE
static void
_dispatch_sync_recurse(dispatch_lane_t dq, void *ctxt,
        dispatch_function_t func, uintptr_t dc_flags)
{
    dispatch_tid tid = _dispatch_tid_self();
    dispatch_queue_t tq = dq->do_targetq;

    do {
        if (likely(tq->dq_width == 1)) {
            if (unlikely(!_dispatch_queue_try_acquire_barrier_sync(tq, tid))) {
                return _dispatch_sync_f_slow(dq, ctxt, func, dc_flags, tq,
                        DC_FLAG_BARRIER);
            }
        } else {
            dispatch_queue_concurrent_t dl = upcast(tq)._dl;
            if (unlikely(!_dispatch_queue_try_reserve_sync_width(dl))) {
                return _dispatch_sync_f_slow(dq, ctxt, func, dc_flags, tq, 0);
            }
        }
        tq = tq->do_targetq;
    } while (unlikely(tq->do_targetq));

    _dispatch_introspection_sync_begin(dq);
    _dispatch_sync_invoke_and_complete_recurse(dq, ctxt, func, dc_flags
            DISPATCH_TRACE_ARG(_dispatch_trace_item_sync_push_pop(
                    dq, ctxt, func, dc_flags)));
}
??
DISPATCH_NOINLINE
static void
_dispatch_sync_invoke_and_complete_recurse(dispatch_queue_class_t dq,
        void *ctxt, dispatch_function_t func, uintptr_t dc_flags
        DISPATCH_TRACE_ARG(void *dc))
{
    _dispatch_sync_function_invoke_inline(dq, ctxt, func);
    _dispatch_trace_item_complete(dc);
    _dispatch_sync_complete_recurse(dq._dq, NULL, dc_flags);
}

這是 _dispatch_sync_f -> _dispatch_sync_f_inline流程,

  1. _dispatch_sync_f_inline中首先判斷dq_width是否為1筐带,返回執(zhí)行柵欄同步函數(shù)實(shí)現(xiàn)
  2. 判斷隊(duì)列類型是否為dispatch_sync
  3. 判斷如果為全局并發(fā)隊(duì)列或者綁定到非調(diào)度線程的隊(duì)列今穿,就進(jìn)行緩慢同步執(zhí)行
  4. 判斷是否執(zhí)行遞歸調(diào)用
    5.開始執(zhí)行同步執(zhí)行,執(zhí)行完成伦籍,回調(diào)block蓝晒。

結(jié)合堆棧分析


dispatch_sync堆棧執(zhí)行

不論串行隊(duì)列下的同步還是并發(fā)隊(duì)列下的同步腮出,調(diào)用堆棧順序相同。相比異步函數(shù)堆棧芝薇,少了許多胚嘲,但在其底層還是做了許多的判斷與操作的。

  • _dispatch_sync_block_with_privdata 流程
#ifdef __BLOCKS__
DISPATCH_NOINLINE
static void
_dispatch_sync_block_with_privdata(dispatch_queue_t dq, dispatch_block_t work,
        uintptr_t dc_flags)
{
    dispatch_block_private_data_t dbpd = _dispatch_block_get_data(work);
    pthread_priority_t op = 0, p = 0;
    dispatch_block_flags_t flags = dbpd->dbpd_flags;

    if (flags & DISPATCH_BLOCK_BARRIER) {
        dc_flags |= DC_FLAG_BLOCK_WITH_PRIVATE_DATA | DC_FLAG_BARRIER;
    } else {
        dc_flags |= DC_FLAG_BLOCK_WITH_PRIVATE_DATA;
    }

    op = _dispatch_block_invoke_should_set_priority(flags, dbpd->dbpd_priority);
    if (op) {
        p = dbpd->dbpd_priority;
    }
    voucher_t ov, v = DISPATCH_NO_VOUCHER;
    if (flags & DISPATCH_BLOCK_HAS_VOUCHER) {
        v = dbpd->dbpd_voucher;
    }
    ov = _dispatch_set_priority_and_voucher(p, v, 0);

    // balanced in d_block_sync_invoke or d_block_wait
    if (os_atomic_cmpxchg2o(dbpd, dbpd_queue, NULL, dq, relaxed)) {
        _dispatch_retain_2(dq);
    }
    if (dc_flags & DC_FLAG_BARRIER) {
        _dispatch_barrier_sync_f(dq, work, _dispatch_block_sync_invoke,
                dc_flags);
    } else {
        _dispatch_sync_f(dq, work, _dispatch_block_sync_invoke, dc_flags);
    }
    _dispatch_reset_priority_and_voucher(op, ov);
}

這是柵欄同步函數(shù)流程洛二,但最終根據(jù)隊(duì)列標(biāo)記判斷不是柵欄函數(shù)還是要走_dispatch_sync_f流程的馋劈。

補(bǔ)充「柵欄函數(shù)」

目的:控制任務(wù)同步執(zhí)行,即使是異步函數(shù)+并發(fā)隊(duì)列
方法:dispatch_barrier_async灭红、dispatch_barrier_sync: 異步侣滩、同步柵欄函數(shù)都是能「阻攔」任務(wù)執(zhí)行。后者的區(qū)別是會(huì)阻塞線程变擒。
注意:柵欄函數(shù)只能控制同意并發(fā)隊(duì)列
使用:

    dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
    /* 1.異步函數(shù) */
    dispatch_async(concurrentQueue, ^{
        sleep(1);
        NSLog(@"異步1:休息一下君珠,再做事!");
    });
    
    /* 2. 柵欄函數(shù) */
    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"柵欄:== %@",[NSThread currentThread]);
    });
    /* 3. 異步函數(shù) */
    dispatch_async(concurrentQueue, ^{
        
        NSLog(@"異步2:終于可以到我干活了嗎");
    });
    NSLog(@"主線程:來活了");

//=========打印=========
[34550:1419250] 主線程:來活了
[34550:1419411] 異步1:休息一下娇斑,再做事策添!
[34550:1419412] 柵欄:== <NSThread: 0x600001bc5900>{number = 3, name = (null)}
[34550:1419412] 異步2:終于可以到我干活了嗎

單例

在平常開發(fā)中,經(jīng)常會(huì)使用到單例設(shè)計(jì)模式毫缆,保證一個(gè)類僅有一個(gè)實(shí)例唯竹,并提供一個(gè)訪問它的全局訪問點(diǎn)。主要解決的問題就是:避免一個(gè)全局使用的類頻繁地創(chuàng)建與銷毀苦丁。
一般編寫單例的形式如下:

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
     //TODO:初始化
});

// --可以查看dispatch_once_t的類型浸颓,它是一個(gè)長(zhǎng)整型,通過取地址&做傳參--- 
typdef long  dispatch_once_t; 

在這里探究當(dāng)然是因?yàn)樗膶?shí)現(xiàn)就是發(fā)生在libdispatch.dylib中的旺拉,所以溯源找到了以下定義

// once.c
void
dispatch_once(dispatch_once_t *val, dispatch_block_t block)
{
    dispatch_once_f(val, block, _dispatch_Block_invoke(block));
}
//------
#define _dispatch_Block_invoke(bb) \
        ( (dispatch_function_t) ((struct Block_layout *)bb)->invoke )
typedef void (*dispatch_function_t)(void *_Nullable);
//------

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;
// 非編譯優(yōu)化 或 采用一次靜默計(jì)數(shù)器方法产上。判斷是否已經(jīng)存在一個(gè)實(shí)例化單例,存在即返回
#if !DISPATCH_ONCE_INLINE_FASTPATH || DISPATCH_ONCE_USE_QUIESCENT_COUNTER
// 即創(chuàng)建一個(gè)標(biāo)記蛾狗,判斷是否等于 DLOCK_ONCE_DONE(表示已經(jīng)創(chuàng)建)
    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
    //☆:嘗試進(jìn)入晋涣;判斷 l(即oncetokden)是否為DLOCK_ONCE_UNLOCKED
    if (_dispatch_once_gate_tryenter(l)) {
        return _dispatch_once_callout(l, ctxt, func);
    }
    return _dispatch_once_wait(l);
}

// ----dispatch_gate_s + dispatch_once_gate_s ----- 
typedef struct dispatch_gate_s {
    dispatch_lock dgl_lock;
} dispatch_gate_s, *dispatch_gate_t;

typedef struct dispatch_once_gate_s {
    union {
        dispatch_gate_s dgo_gate;
        uintptr_t dgo_once; // dgo_once 作為判斷已存在單例的標(biāo)志
    };
} dispatch_once_gate_s, *dispatch_once_gate_t;


  • dispatch_once_t是長(zhǎng)整型,定義成了一個(gè)靜態(tài)變量沉桌,首先就它而已是具有唯一性谢鹊。強(qiáng)轉(zhuǎn)類型之后得到l, &l->dgo_once就作為判斷是否已經(jīng)創(chuàng)建實(shí)例化的標(biāo)志。
  • #define DISPATCH_ONCE_IS_GEN(gen) (((gen) & 3) == DLOCK_FAILED_TRYLOCK_BIT) 留凭,在采用靜默計(jì)數(shù)器方法情況下佃扼,嘗試加鎖,加鎖失敗之后會(huì)執(zhí)行_dispatch_once_mark_done_if_quiesced蔼夜。
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.
         */
        // 再次存儲(chǔ)標(biāo)志松嘶,存儲(chǔ)值為:DLOCK_ONCE_DONE. 
        os_atomic_store(&dgo->dgo_once, DLOCK_ONCE_DONE, relaxed);
    }
}
  • 正常單例初始化創(chuàng)建,第一次就會(huì)走這個(gè)流程_dispatch_once_gate_tryenter挎扰,嘗試進(jìn)入執(zhí)行創(chuàng)建
DISPATCH_ALWAYS_INLINE
static inline bool
_dispatch_once_gate_tryenter(dispatch_once_gate_t l)
{
    // 先對(duì)比&l->dgo_once是否等于無鎖DLOCK_ONCE_UNLOCKED翠订;
    // 1、如果相等則更改其值遵倦,為_dispatch_lock_value_for_self() (即給自己加鎖)尽超,返回YES
    // 2、如果不等梧躺,則DLOCK_ONCE_UNLOCKED賦值給l->dgo_once似谁,最后NO
    return os_atomic_cmpxchg(&l->dgo_once, DLOCK_ONCE_UNLOCKED,
            (uintptr_t)_dispatch_lock_value_for_self(), relaxed);
}
#define os_atomic_cmpxchg(p, e, v, m) \
        ({ _os_atomic_basetypeof(p) _r = (e); \
        atomic_compare_exchange_strong_explicit(_os_atomic_c11_atomic(p), \
        &_r, v, memory_order_##m, memory_order_relaxed); })

嘗試對(duì)比l->dgo_once 與 DLOCK_ONCE_UNLOCKED是否相同.根據(jù)對(duì)比結(jié)果返回YES\NO. 如果為YES,則_dispatch_once_callout執(zhí)行block回調(diào)掠哥,同時(shí)向外廣播狀態(tài)巩踏,拒絕再次訪問創(chuàng)建,续搀。為NO塞琼,則執(zhí)行_dispatch_once_wait,即在當(dāng)前實(shí)例創(chuàng)建過程中,會(huì)加鎖禁舷,當(dāng)有需求再次創(chuàng)建時(shí)彪杉,會(huì)一直等待,除非鎖被釋放牵咙。

DISPATCH_NOINLINE
static void
_dispatch_once_callout(dispatch_once_gate_t l, void *ctxt,
        dispatch_function_t func)
{
    // 執(zhí)行方法塊 func派近,即block
    _dispatch_client_callout(ctxt, func);
    // 對(duì)外發(fā)送廣播,當(dāng)前已經(jīng)有執(zhí)行單例創(chuàng)建洁桌,拒絕再訪問
    _dispatch_once_gate_broadcast(l);
}

// --_dispatch_client_callout--
#undef _dispatch_client_callout
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
    @try {
        return f(ctxt);
    }
    @catch (...) {
        objc_terminate();
    }
}

// --_dispatch_once_gate_broadcast--
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);//標(biāo)記為停頓
#else
    v = _dispatch_once_mark_done(l); // 標(biāo)記 DLOCK_ONCE_DONE
#endif
    // 如果 v已經(jīng)是自鎖狀態(tài)渴丸,即已經(jīng)是初始化創(chuàng)建過單例對(duì)象 l(onceToken) = DLOCK_ONCE_DONE,就直接返回
    if (likely((dispatch_lock)v == value_self)) return;
    _dispatch_gate_broadcast_slow(&l->dgo_gate, (dispatch_lock)v);
}

// ----_dispatch_once_mark_done--
DISPATCH_ALWAYS_INLINE
static inline uintptr_t
_dispatch_once_mark_done(dispatch_once_gate_t dgo)
{
    //DLOCK_ONCE_DONE 賦值給 dgo->dgo_once 另凌,然后上鎖谱轨,標(biāo)記DLOCK_ONCE_DONE(-1),即onceToken 的值為 DLOCK_ONCE_DONE途茫。
    return os_atomic_xchg(&dgo->dgo_once, DLOCK_ONCE_DONE, release);
}
// 
#define os_atomic_xchg(p, v, m) \
        atomic_exchange_explicit(_os_atomic_c11_atomic(p), v, memory_order_##m)

總結(jié)

【單例流程】先判斷l是否第一次碟嘴,如果為第一次來,則將DLOCK_ONCE_UNLOCKED賦值給 l->dgo_once囊卜,先加鎖_dispatch_lock_value_for_self()娜扇;返回YES執(zhí)行_dispatch_once_callout調(diào)用block創(chuàng)建;
第二次時(shí)栅组,即實(shí)例已經(jīng)創(chuàng)建雀瓢,判斷os_atomic_load ()即 v是否等于一個(gè) DLOCK_ONCE_DONE,如果等于玉掸,直接return刃麸。

【線程安全】有多個(gè)線程進(jìn)來時(shí),dispatch_lock_value_for_self()加鎖了司浪,保證了同一時(shí)間僅有一個(gè)線程在處理泊业。當(dāng)前線程未處理外把沼,后來者將保持等待狀態(tài)_dispatch_once_wait(),直到鎖解除吁伺。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末饮睬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子篮奄,更是在濱河造成了極大的恐慌捆愁,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窟却,死亡現(xiàn)場(chǎng)離奇詭異昼丑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)夸赫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門菩帝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人憔足,你說我怎么就攤上這事胁附。” “怎么了滓彰?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵控妻,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我揭绑,道長(zhǎng)弓候,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任他匪,我火速辦了婚禮菇存,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘邦蜜。我一直安慰自己依鸥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布悼沈。 她就那樣靜靜地躺著贱迟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪絮供。 梳的紋絲不亂的頭發(fā)上衣吠,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音壤靶,去河邊找鬼缚俏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的忧换。 我是一名探鬼主播恬惯,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼包雀!你這毒婦竟也來了宿崭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤才写,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后奖蔓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赞草,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年吆鹤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了厨疙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疑务,死狀恐怖沾凄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情知允,我是刑警寧澤撒蟀,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站温鸽,受9級(jí)特大地震影響保屯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜涤垫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一姑尺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蝠猬,春花似錦切蟋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至歧杏,卻和暖如春镰惦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背犬绒。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工旺入, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓茵瘾,卻偏偏與公主長(zhǎng)得像礼华,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拗秘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • iOS 底層原理 文章匯總[http://www.reibang.com/p/412b20d9a0f6] 本文是...
    Style_月月閱讀 4,917評(píng)論 14 15
  • libdispatch 源碼下載地址[https://opensource.apple.com/tarballs/...
    _zhang__閱讀 789評(píng)論 0 1
  • GCD 介紹 什么是GCD雕旨? 全稱是Grand Central Dispatch 純C語?扮匠,提供了?常多強(qiáng)?的函數(shù)...
    北京_小海閱讀 344評(píng)論 1 1
  • 久違的晴天,家長(zhǎng)會(huì)凡涩。 家長(zhǎng)大會(huì)開好到教室時(shí)棒搜,離放學(xué)已經(jīng)沒多少時(shí)間了。班主任說已經(jīng)安排了三個(gè)家長(zhǎng)分享經(jīng)驗(yàn)活箕。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,523評(píng)論 16 22
  • 創(chuàng)業(yè)是很多人的夢(mèng)想力麸,多少人為了理想和不甘選擇了創(chuàng)業(yè)來實(shí)現(xiàn)自我價(jià)值,我就是其中一個(gè)育韩。 創(chuàng)業(yè)后克蚂,我由女人變成了超人,什...
    亦寶寶閱讀 1,812評(píng)論 4 1