多線程原理--GCD源碼分析

多線程原理--GCD源碼分析

閱讀源碼是枯燥的馅袁,可能暫時(shí)對(duì)我們的工作沒什么幫助,現(xiàn)在但是作為一個(gè)有一定開發(fā)經(jīng)驗(yàn)的開發(fā)人員而言,這一步是必須要走的韧涨;可能是受到了身邊同事、同行的影響蝙场,看別人在讀源碼也跟著讀源碼,或者是開發(fā)中遇到了瓶頸,亦或者是開發(fā)不再局限于業(yè)務(wù)的開發(fā)未檩,需要架構(gòu)設(shè)計(jì)、設(shè)計(jì)模式以及數(shù)據(jù)結(jié)構(gòu)和算法等需要閱讀源碼等等粟焊,一般開始的時(shí)候真的很難讀懂冤狡,看的頭大,但是當(dāng)我們用盡辦法研究通后项棠,那個(gè)時(shí)候真的很爽悲雳。我們不再只是知道這樣寫,我們可以知其然知其所以然香追,知道有些函數(shù)是做什么的合瓢,知道其底層原理是怎樣的,比如同樣實(shí)現(xiàn)一個(gè)功能可以用很多種方法透典,我們知道這些方法底層原理后可以知道這些方法的本質(zhì)區(qū)別晴楔,我們可以通過閱讀源碼學(xué)習(xí)到一些更好的設(shè)計(jì)思想、有更好的問題解決方案等等峭咒,也可以鍛煉我們的耐心和毅力税弃,閱讀源碼對(duì)我們來(lái)說(shuō)真的受益無(wú)窮。

如果還不是很了解GCD凑队,可以先簡(jiǎn)單了解一下GCD:多線程原理--了解GCD则果,接下來(lái)開始分析當(dāng)前最新版本的源碼:libdispatch-1008.200.78.tar.gz,建議去獲取最新版本GCD源碼:opensource源或者github源顽决。

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

我們可以探索下串行和并發(fā)隊(duì)列的區(qū)別短条。
首先跟著創(chuàng)建隊(duì)列函數(shù)dispatch_queue_create進(jìn)入源碼,除了我們賦值的label和attr才菠,系統(tǒng)還將tq賦值DISPATCH_TARGET_QUEUE_DEFAULT茸时,legacy 賦值為true傳給dispatch_queue_create_with_target,其內(nèi)部首先通過_dispatch_queue_attr_to_info和我們傳進(jìn)來(lái)的attr來(lái)初始化dispatch_queue_attr_info_t赋访。

dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
{
    return _dispatch_lane_create_with_target(label, attr,
            DISPATCH_TARGET_QUEUE_DEFAULT, true);
}

---------------------

static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
        dispatch_queue_t tq, bool legacy)
{
    dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
  // ...省略N行代碼--部分代碼
    const void *vtable;
    dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
    if (dqai.dqai_concurrent) {
        vtable = DISPATCH_VTABLE(queue_concurrent);
    } else {
        vtable = DISPATCH_VTABLE(queue_serial);
    }

    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s));
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));
    dq->dq_label = label;
    dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,
            dqai.dqai_relpri);
    // 自定義的queue的目標(biāo)隊(duì)列是root隊(duì)列
    dq->do_targetq = tq;
    _dispatch_object_debug(dq, "%s", __func__);
    return _dispatch_trace_queue_create(dq)._dq;

再次通過全局搜索_dispatch_queue_attr_to_info可都,來(lái)查看_dispatch_queue_attr_to_info內(nèi)部的實(shí)現(xiàn)。

dispatch_queue_attr_info_t
_dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa)
{
    dispatch_queue_attr_info_t dqai = { };

    if (!dqa) return dqai;

#if DISPATCH_VARIANT_STATIC
    if (dqa == &_dispatch_queue_attr_concurrent) {
        dqai.dqai_concurrent = true;
        return dqai;
    }
#endif

    if (dqa < _dispatch_queue_attrs ||
            dqa >= &_dispatch_queue_attrs[DISPATCH_QUEUE_ATTR_COUNT]) {
        DISPATCH_CLIENT_CRASH(dqa->do_vtable, "Invalid queue attribute");
    }

    size_t idx = (size_t)(dqa - _dispatch_queue_attrs);

    dqai.dqai_inactive = (idx % DISPATCH_QUEUE_ATTR_INACTIVE_COUNT);
    idx /= DISPATCH_QUEUE_ATTR_INACTIVE_COUNT;

    dqai.dqai_concurrent = !(idx % DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT);
    idx /= DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT;

    dqai.dqai_relpri = -(idx % DISPATCH_QUEUE_ATTR_PRIO_COUNT);
    idx /= DISPATCH_QUEUE_ATTR_PRIO_COUNT;

    dqai.dqai_qos = idx % DISPATCH_QUEUE_ATTR_QOS_COUNT;
    idx /= DISPATCH_QUEUE_ATTR_QOS_COUNT;

    dqai.dqai_autorelease_frequency =
            idx % DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;
    idx /= DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;

    dqai.dqai_overcommit = idx % DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;
    idx /= DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;

    return dqai;
}

_dispatch_queue_attr_to_info方法內(nèi)部首先判斷我們傳進(jìn)來(lái)的dqa是否為空蚓耽,如果為空則直接返回空結(jié)構(gòu)體渠牲,也就是我們所說(shuō)的串行隊(duì)列(串行隊(duì)列我們一般傳DISPATCH_QUEUE_SERIAL或者NULL,其實(shí)DISPATCH_QUEUE_SERIAL的宏定義就是NULL)步悠。如果不為空签杈,則進(jìn)入蘋果的算法,通過結(jié)構(gòu)體位域來(lái)設(shè)置dqai的屬性并返回該結(jié)構(gòu)體dispatch_queue_attr_info_t。結(jié)構(gòu)體:

typedef struct dispatch_queue_attr_info_s {
    dispatch_qos_t dqai_qos : 8;
    int      dqai_relpri : 8;
    uint16_t dqai_overcommit:2;
    uint16_t dqai_autorelease_frequency:2;
    uint16_t dqai_concurrent:1;
    uint16_t dqai_inactive:1;
} dispatch_queue_attr_info_t;

再次回到_dispatch_lane_create_with_target內(nèi)部答姥,接下來(lái)出來(lái)overcommit(如果是串行隊(duì)列的話默認(rèn)是開啟的铣除,并行是關(guān)閉的)、_dispatch_get_root_queue獲取一個(gè)管理自己隊(duì)列的root隊(duì)列鹦付,每一個(gè)優(yōu)先級(jí)都有對(duì)應(yīng)的root隊(duì)列尚粘,每一個(gè)優(yōu)先級(jí)又分為是不是可以過載的隊(duì)列。再通過dqai.dqai_concurrent來(lái)區(qū)分并發(fā)和串行敲长,DISPATCH_VTABLE內(nèi)部利用OS_dispatch_##name##_class生成相應(yīng)的class保存到結(jié)構(gòu)體dispatch_queue_t內(nèi)的do_vtable變量郎嫁,接下來(lái)開辟內(nèi)存_dispatch_object_alloc、構(gòu)造方法_dispatch_queue_init這里的第三個(gè)參數(shù)判斷是否并行隊(duì)列祈噪,如果不是則最多開辟一條線程泽铛,如果是并行隊(duì)列則最多可以開辟DISPATCH_QUEUE_WIDTH_FULL(0x1000) - 2條,也就是0xffe換算成10進(jìn)制就是4094條線程钳降,接下來(lái)就是設(shè)置dq的dq_label厚宰、dq_priority等屬性腌巾,最后返回_dispatch_trace_queue_create(dq)._dq遂填。進(jìn)入其內(nèi)部再次返回_dispatch_introspection_queue_create(dqu),直到進(jìn)入_dispatch_introspection_queue_create_hook內(nèi)部的dispatch_introspection_queue_get_info返回串行或者并行的結(jié)構(gòu)體用來(lái)保存關(guān)于隊(duì)列的信息澈蝙。dispatch_introspection_queue_s

dispatch_introspection_queue_s diq = {
        .queue = dq->_as_dq,
        .target_queue = dq->do_targetq,
        .label = dq->dq_label,
        .serialnum = dq->dq_serialnum,
        .width = dq->dq_width,
        .suspend_count = _dq_state_suspend_cnt(dq_state) + dq->dq_side_suspend_cnt,
        .enqueued = _dq_state_is_enqueued(dq_state) && !global,
        .barrier = _dq_state_is_in_barrier(dq_state) && !global,
        .draining = (dq->dq_items_head == (void*)~0ul) ||
                (!dq->dq_items_head && dq->dq_items_tail),
        .global = global,
        .main = dx_type(dq) == DISPATCH_QUEUE_MAIN_TYPE,
    };

---------

dispatch_introspection_queue_s diq = {
        .queue = dwl->_as_dq,
        .target_queue = dwl->do_targetq,
        .label = dwl->dq_label,
        .serialnum = dwl->dq_serialnum,
        .width = 1,
        .suspend_count = 0,
        .enqueued = _dq_state_is_enqueued(dq_state),
        .barrier = _dq_state_is_in_barrier(dq_state),
        .draining = 0,
        .global = 0,
        .main = 0,
    };

對(duì)于dispatch_get_global_queue從底層_dispatch_get_root_queue中取得合適的隊(duì)列吓坚,其可以開辟DISPATCH_QUEUE_WIDTH_FULL(0x1000) - 1條線程,也就是0xfff灯荧,并且從dispatch_queue_s _dispatch_root_queues[]全局屬性里面存放各種global_queue礁击;而對(duì)于dispatch_get_main_queue則是通過DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);返回,通過全局搜索

DISPATCH_DECL_SUBCLASS(dispatch_queue_main, dispatch_queue_serial);
#define OS_OBJECT_DECL_SUBCLASS(name, super) \
        OS_OBJECT_DECL_IMPL(name, <OS_OBJECT_CLASS(super)>)

可以發(fā)現(xiàn)dispatch_queue_main就是串行dispatch_queue_serial的子類逗载,線程的width同樣是0x1哆窿,也就是只有1條。

同步dispatch_sync

接下來(lái)研究一下同步函數(shù)dispatch_sync厉斟,查看其源碼進(jìn)入內(nèi)部方法_dispatch_sync_f挚躯,再次進(jìn)入_dispatch_sync_f_inline內(nèi)部:

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)) {
        return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags);
    }

    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
    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)) {
        return _dispatch_sync_recurse(dl, ctxt, func, dc_flags);
    }
    _dispatch_introspection_sync_begin(dl);
    _dispatch_sync_invoke_and_complete(dl, ctxt, func DISPATCH_TRACE_ARG(
            _dispatch_trace_item_sync_push_pop(dq, ctxt, func, dc_flags)));
}

首先判斷dq_width是否等于1,,也就是當(dāng)前隊(duì)列是否是串行隊(duì)列擦秽,如果是則執(zhí)行_dispatch_barrier_sync_f码荔,經(jīng)過一系列的嵌套最終走到_dispatch_barrier_sync_f_inline_dispatch_barrier_sync_f_inline內(nèi)部先通過_dispatch_thread_port獲取當(dāng)前線程ID感挥,進(jìn)入_dispatch_queue_try_acquire_barrier_sync判斷線程狀態(tài)缩搅,進(jìn)入內(nèi)部_dispatch_queue_try_acquire_barrier_sync_and_suspend,在這里會(huì)通過os_atomic_rmw_loop2o來(lái)獲取當(dāng)前隊(duì)列依賴線程的狀態(tài)信息触幼;如果判斷當(dāng)前隊(duì)列是全局并行隊(duì)列或者綁定的是非調(diào)度線程的隊(duì)列會(huì)直接進(jìn)入if判斷內(nèi)執(zhí)行_dispatch_sync_f_slow硼瓣,在_dispatch_sync_f_slow內(nèi)部會(huì)執(zhí)行同步等待__DISPATCH_WAIT_FOR_QUEUE__,這里涉及到死鎖的問題置谦,其內(nèi)部會(huì)將等待的隊(duì)列_dispatch_wait_prepare和當(dāng)前調(diào)度的隊(duì)列進(jìn)行對(duì)比_dq_state_drain_locked_by(dq_state, dsc->dsc_waiter)堂鲤,如果相同則直接拋出crash:"dispatch_sync called on queue " "already owned by current thread"噪猾;如果沒有產(chǎn)生死鎖,最后執(zhí)行_dispatch_sync_invoke_and_complete_recurse筑累,其內(nèi)部先執(zhí)行_dispatch_thread_frame_push把任務(wù)壓棧到隊(duì)列后再執(zhí)行func(block任務(wù))后mach底層通過hook函數(shù)來(lái)監(jiān)聽complete袱蜡,再_dispatch_thread_frame_pop把任務(wù)pop出去,這也就是為什么同步并發(fā)會(huì)順序執(zhí)行的原因慢宗。

_dispatch_barrier_sync_f_inline

DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_barrier_sync_f_inline(dispatch_queue_t dq, void *ctxt,
        dispatch_function_t func, uintptr_t dc_flags)
{
    dispatch_tid tid = _dispatch_tid_self();

    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;
    // The more correct thing to do would be to merge the qos of the thread
    // that just acquired the barrier lock into the queue state.
    //
    // However this is too expensive for the fast path, so skip doing it.
    // The chosen tradeoff is that if an enqueue on a lower priority thread
    // contends with this fast path, this thread may receive a useless override.
    //
    // Global concurrent queues and queues bound to non-dispatch threads
    // always fall into the slow case, see DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE
    
    if (unlikely(!_dispatch_queue_try_acquire_barrier_sync(dl, tid))) {
        return _dispatch_sync_f_slow(dl, ctxt, func, DC_FLAG_BARRIER, dl,
                DC_FLAG_BARRIER | dc_flags);
    }

    if (unlikely(dl->do_targetq->do_targetq)) {
        return _dispatch_sync_recurse(dl, ctxt, func,
                DC_FLAG_BARRIER | dc_flags);
    }
    _dispatch_introspection_sync_begin(dl);
    _dispatch_lane_barrier_sync_invoke_and_complete(dl, ctxt, func
            DISPATCH_TRACE_ARG(_dispatch_trace_item_sync_push_pop(
                    dq, ctxt, func, dc_flags | DC_FLAG_BARRIER)));
}

DISPATCH_NOINLINE
static void
_dispatch_barrier_sync_f(dispatch_queue_t dq, void *ctxt,
        dispatch_function_t func, uintptr_t dc_flags)
{
    _dispatch_barrier_sync_f_inline(dq, ctxt, func, dc_flags);
}

如果是全局并行隊(duì)列或者綁定的是非調(diào)度線程的隊(duì)列會(huì)直接進(jìn)入_dispatch_sync_f_slow和上述邏輯相同坪蚁。如果是加入柵欄函數(shù)的則開始驗(yàn)證target是否存在,_dispatch_sync_recurse內(nèi)遞歸_dispatch_sync_wait進(jìn)行查找target镜沽,直到找到target后執(zhí)行_dispatch_sync_invoke_and_complete_recurse完成回調(diào)敏晤。

異步dispatch_async

進(jìn)入dispatch_async源碼內(nèi)部,先進(jìn)行了初始化操作:

void
dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
{
    dispatch_continuation_t dc = _dispatch_continuation_alloc();
    uintptr_t dc_flags = DC_FLAG_CONSUME;
    dispatch_qos_t qos;

    qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
    _dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}

進(jìn)入_dispatch_continuation_init內(nèi)部將dispatch_async的block任務(wù)重新賦值給func并保持為dc的dc_func屬性缅茉。接下來(lái)執(zhí)行_dispatch_continuation_async嘴脾,最后進(jìn)入_dispatch_continuation_async內(nèi)部的dx_push,通過宏定義#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)蔬墩,我們選擇進(jìn)入全局并發(fā)隊(duì)列_dispatch_root_queue_push译打,最終進(jìn)入_dispatch_root_queue_poke_slow

static void
_dispatch_root_queue_poke_slow(dispatch_queue_global_t dq, int n, int floor)
{
    int remaining = n;
    int r = ENOSYS;

    _dispatch_root_queues_init();
    _dispatch_debug_root_queue(dq, __func__);
    _dispatch_trace_runtime_event(worker_request, dq, (uint64_t)n);

#if !DISPATCH_USE_INTERNAL_WORKQUEUE
#if DISPATCH_USE_PTHREAD_ROOT_QUEUES
    if (dx_type(dq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE)
#endif
    {
        _dispatch_root_queue_debug("requesting new worker thread for global "
                "queue: %p", dq);
        r = _pthread_workqueue_addthreads(remaining,
                _dispatch_priority_to_pp_prefer_fallback(dq->dq_priority));
        (void)dispatch_assume_zero(r);
        return;
    }
#endif // !DISPATCH_USE_INTERNAL_WORKQUEUE
#if DISPATCH_USE_PTHREAD_POOL
    dispatch_pthread_root_queue_context_t pqc = dq->do_ctxt;
    if (likely(pqc->dpq_thread_mediator.do_vtable)) {
        while (dispatch_semaphore_signal(&pqc->dpq_thread_mediator)) {
            _dispatch_root_queue_debug("signaled sleeping worker for "
                    "global queue: %p", dq);
            if (!--remaining) {
                return;
            }
        }
    }

    bool overcommit = dq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
    if (overcommit) {
        os_atomic_add2o(dq, dgq_pending, remaining, relaxed);
    } else {
        if (!os_atomic_cmpxchg2o(dq, dgq_pending, 0, remaining, relaxed)) {
            _dispatch_root_queue_debug("worker thread request still pending for "
                    "global queue: %p", dq);
            return;
        }
    }

    int can_request, t_count;
    // seq_cst with atomic store to tail <rdar://problem/16932833>
    t_count = os_atomic_load2o(dq, dgq_thread_pool_size, ordered);
    do {
        can_request = t_count < floor ? 0 : t_count - floor;
        if (remaining > can_request) {
            _dispatch_root_queue_debug("pthread pool reducing request from %d to %d",
                    remaining, can_request);
            os_atomic_sub2o(dq, dgq_pending, remaining - can_request, relaxed);
            remaining = can_request;
        }
        if (remaining == 0) {
            _dispatch_root_queue_debug("pthread pool is full for root queue: "
                    "%p", dq);
            return;
        }
    } while (!os_atomic_cmpxchgvw2o(dq, dgq_thread_pool_size, t_count,
            t_count - remaining, &t_count, acquire));

    pthread_attr_t *attr = &pqc->dpq_thread_attr;
    pthread_t tid, *pthr = &tid;
#if DISPATCH_USE_MGR_THREAD && DISPATCH_USE_PTHREAD_ROOT_QUEUES
    if (unlikely(dq == &_dispatch_mgr_root_queue)) {
        pthr = _dispatch_mgr_root_queue_init();
    }
#endif
    do {
        _dispatch_retain(dq); // released in _dispatch_worker_thread
        while ((r = pthread_create(pthr, attr, _dispatch_worker_thread, dq))) {
            if (r != EAGAIN) {
                (void)dispatch_assume_zero(r);
            }
            _dispatch_temporary_resource_shortage();
        }
    } while (--remaining);
#else
    (void)floor;
#endif // DISPATCH_USE_PTHREAD_POOL
}

_dispatch_root_queue_poke_slow先判斷當(dāng)前隊(duì)列是否有問題,接下來(lái)執(zhí)行_pthread_workqueue_addthreads調(diào)用底層直接添加線程到工作隊(duì)列拇颅;下面第一個(gè)do-while循環(huán)來(lái)判斷當(dāng)前隊(duì)列的緩存池的大小能否繼續(xù)申請(qǐng)線程奏司,如果大于可申請(qǐng)的大小則出現(xiàn)容積崩潰_dispatch_root_queue_debug("pthread pool reducing request from %d to %d", remaining, can_request);,如果等于0樟插,則報(bào)_dispatch_root_queue_debug("pthread pool is full for root queue: " "%p", dq);韵洋。如果可以開辟的話,則進(jìn)入下一個(gè)do-while循環(huán)黄锤,這時(shí)我們可以發(fā)現(xiàn)全局并發(fā)隊(duì)列pthread_create來(lái)創(chuàng)建線程搪缨,直到要?jiǎng)?chuàng)建的線程為0。

單例dispatch_once

進(jìn)入dispatch_once源碼內(nèi)部dispatch_once_f方法內(nèi)鸵熟,首先對(duì)dispatch_once_t做標(biāo)記副编,如果當(dāng)前狀態(tài)為DLOCK_ONCE_DONE說(shuō)明有加載過下次就不再次加載;如果從來(lái)沒加載過則進(jìn)入_dispatch_once_gate_tryenter旅赢,如果當(dāng)前狀態(tài)是DLOCK_ONCE_UNLOCKED則執(zhí)行_dispatch_once_callout內(nèi)部通過_dispatch_client_callout來(lái)進(jìn)行單例調(diào)用齿桃,_dispatch_once_gate_broadcast來(lái)做DLOCK_ONCE_DONE標(biāo)記已經(jīng)加載過。

void
dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
    dispatch_once_gate_t l = (dispatch_once_gate_t)val;
    //DLOCK_ONCE_DONE
#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)) {
        // 單利調(diào)用 -- v->DLOCK_ONCE_DONE
        return _dispatch_once_callout(l, ctxt, func);
    }
    return _dispatch_once_wait(l);
}

信號(hào)量dispatch_semaphore

首先創(chuàng)建信號(hào)量dispatch_semaphore_create源碼內(nèi)部主要是初始化信號(hào)量的信息和保存信號(hào)量dsema_value煮盼。接下來(lái)進(jìn)入等待信號(hào)量dispatch_wait源碼內(nèi)部dispatch_semaphore_wait短纵,先執(zhí)行os_atomic_dec2o對(duì)信號(hào)量-1操作后,再判斷當(dāng)前信號(hào)量如果大于等于0則直接返回僵控,否則進(jìn)入等待_dispatch_semaphore_wait_slow邏輯香到,其內(nèi)部會(huì)一直等待直到信號(hào)量為0或者調(diào)用semaphore_signal()才能喚醒。

long
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
{
    long value = os_atomic_dec2o(dsema, dsema_value, acquire);
    if (likely(value >= 0)) {
        return 0;
    }
    return _dispatch_semaphore_wait_slow(dsema, timeout);
}

再看dispatch_semaphore_signal的源碼內(nèi)部實(shí)現(xiàn),首先等待信號(hào)量dispatch_wait正好相反悠就,執(zhí)行os_atomic_inc2o對(duì)信號(hào)量+1操作千绪。

long
dispatch_semaphore_signal(dispatch_semaphore_t dsema)
{
    long value = os_atomic_inc2o(dsema, dsema_value, release);
    if (likely(value > 0)) {
        return 0;
    }
    if (unlikely(value == LONG_MIN)) {
        DISPATCH_CLIENT_CRASH(value,
                "Unbalanced call to dispatch_semaphore_signal()");
    }
    return _dispatch_semaphore_signal_slow(dsema);
}

long
_dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema)
{
    _dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
    _dispatch_sema4_signal(&dsema->dsema_sema, 1);
    return 1;
}

調(diào)度組dispatch_group

首先進(jìn)入dispatch_group_create源碼內(nèi)部,利用_dispatch_object_alloc來(lái)創(chuàng)建dispatch_group_t并初始化梗脾,最后返回荸型。接下來(lái)看dispatch_group_enter,其內(nèi)部先通過os_atomic_sub_orig2o來(lái)進(jìn)行-1操作炸茧,dispatch_group_leave則是進(jìn)行+1操作瑞妇,這里可以看到如果進(jìn)行dispatch_group_enter操作信號(hào)量不為0或者進(jìn)行dispatch_group_leave操作后信號(hào)量等于0,則說(shuō)明dispatch_group_enterdispatch_group_leave不是匹配的梭冠,那么直接報(bào)出DISPATCH_CLIENT_CRASH信息辕狰。如果目前沒問題的話那么dispatch_group_leave會(huì)執(zhí)行_dispatch_group_wake

DISPATCH_ALWAYS_INLINE
static inline dispatch_group_t
_dispatch_group_create_with_count(uint32_t n)
{
    dispatch_group_t dg = _dispatch_object_alloc(DISPATCH_VTABLE(group),
            sizeof(struct dispatch_group_s));
    dg->do_next = DISPATCH_OBJECT_LISTLESS;
    dg->do_targetq = _dispatch_get_default_queue(false);
    if (n) {
        os_atomic_store2o(dg, dg_bits,
                -n * DISPATCH_GROUP_VALUE_INTERVAL, relaxed);
        os_atomic_store2o(dg, do_ref_cnt, 1, relaxed); // <rdar://22318411>
    }
    return dg;
}

void
dispatch_group_leave(dispatch_group_t dg)
{
    // The value is incremented on a 64bits wide atomic so that the carry for
    // the -1 -> 0 transition increments the generation atomically.
    uint64_t new_state, old_state = os_atomic_add_orig2o(dg, dg_state,
            DISPATCH_GROUP_VALUE_INTERVAL, release);
    uint32_t old_value = (uint32_t)(old_state & DISPATCH_GROUP_VALUE_MASK);

    if (unlikely(old_value == DISPATCH_GROUP_VALUE_1)) {
        old_state += DISPATCH_GROUP_VALUE_INTERVAL;
        do {
            new_state = old_state;
            if ((old_state & DISPATCH_GROUP_VALUE_MASK) == 0) {
                new_state &= ~DISPATCH_GROUP_HAS_WAITERS;
                new_state &= ~DISPATCH_GROUP_HAS_NOTIFS;
            } else {
                // If the group was entered again since the atomic_add above,
                // we can't clear the waiters bit anymore as we don't know for
                // which generation the waiters are for
                new_state &= ~DISPATCH_GROUP_HAS_NOTIFS;
            }
            if (old_state == new_state) break;
        } while (unlikely(!os_atomic_cmpxchgv2o(dg, dg_state,
                old_state, new_state, &old_state, relaxed)));
        return _dispatch_group_wake(dg, old_state, true);
    }

    if (unlikely(old_value == 0)) {
        DISPATCH_CLIENT_CRASH((uintptr_t)old_value,
                "Unbalanced call to dispatch_group_leave()");
    }
}

void
dispatch_group_enter(dispatch_group_t dg)
{
    // The value is decremented on a 32bits wide atomic so that the carry
    // for the 0 -> -1 transition is not propagated to the upper 32bits.
    uint32_t old_bits = os_atomic_sub_orig2o(dg, dg_bits,
            DISPATCH_GROUP_VALUE_INTERVAL, acquire);
    uint32_t old_value = old_bits & DISPATCH_GROUP_VALUE_MASK;
    if (unlikely(old_value == 0)) {
        _dispatch_retain(dg); // <rdar://problem/22318411>
    }
    if (unlikely(old_value == DISPATCH_GROUP_VALUE_MAX)) {
        DISPATCH_CLIENT_CRASH(old_bits,
                "Too many nested calls to dispatch_group_enter()");
    }
}

_dispatch_group_wake內(nèi)部會(huì)通過do-while執(zhí)行_dispatch_continuation_async來(lái)循環(huán)遍歷添加到notify內(nèi)的任務(wù)控漠。這里dispatch_group_leave后和_dispatch_group_notify最后的操作一樣都會(huì)調(diào)用_dispatch_group_wake來(lái)執(zhí)行任務(wù)蔓倍。

DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_group_notify(dispatch_group_t dg, dispatch_queue_t dq,
        dispatch_continuation_t dsn)
{
    uint64_t old_state, new_state;
    dispatch_continuation_t prev;

    dsn->dc_data = dq;
    _dispatch_retain(dq);

    prev = os_mpsc_push_update_tail(os_mpsc(dg, dg_notify), dsn, do_next);
    if (os_mpsc_push_was_empty(prev)) _dispatch_retain(dg);
    os_mpsc_push_update_prev(os_mpsc(dg, dg_notify), prev, dsn, do_next);
    if (os_mpsc_push_was_empty(prev)) {
        os_atomic_rmw_loop2o(dg, dg_state, old_state, new_state, release, {
            new_state = old_state | DISPATCH_GROUP_HAS_NOTIFS;
            if ((uint32_t)old_state == 0) {
                os_atomic_rmw_loop_give_up({
                    return _dispatch_group_wake(dg, new_state, false);
                });
            }
        });
    }
}

DISPATCH_NOINLINE
static void
_dispatch_group_wake(dispatch_group_t dg, uint64_t dg_state, bool needs_release)
{
    uint16_t refs = needs_release ? 1 : 0; // <rdar://problem/22318411>

    if (dg_state & DISPATCH_GROUP_HAS_NOTIFS) {
        dispatch_continuation_t dc, next_dc, tail;

        // Snapshot before anything is notified/woken <rdar://problem/8554546>
        dc = os_mpsc_capture_snapshot(os_mpsc(dg, dg_notify), &tail);
        do {
            dispatch_queue_t dsn_queue = (dispatch_queue_t)dc->dc_data;
            next_dc = os_mpsc_pop_snapshot_head(dc, tail, do_next);
            _dispatch_continuation_async(dsn_queue, dc,
                    _dispatch_qos_from_pp(dc->dc_priority), dc->dc_flags);
            _dispatch_release(dsn_queue);
        } while ((dc = next_dc));

        refs++;
    }

    if (dg_state & DISPATCH_GROUP_HAS_WAITERS) {
        _dispatch_wake_by_address(&dg->dg_gen);
    }

    if (refs) _dispatch_release_n(dg, refs);
}

說(shuō)到調(diào)度組肯定少不了dispatch_group_asyncdispatch_group_async其實(shí)就是對(duì)dispatch_group_enterdispatch_group_leave的封裝盐捷。進(jìn)入dispatch_group_async源碼在初始化_dispatch_continuation_init保存任務(wù)后開始執(zhí)行_dispatch_continuation_group_async操作偶翅,我們可以看到內(nèi)部先進(jìn)行了dispatch_group_enter,然后經(jīng)過_dispatch_continuation_async毙驯、dx_push倒堕、_dispatch_root_queue_poke等操作后最終調(diào)用_dispatch_client_callout執(zhí)行任務(wù),當(dāng)任務(wù)執(zhí)行完畢后再通過mach底層來(lái)通知完成complete操作爆价,最后執(zhí)行dispatch_group_leave

DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_continuation_group_async(dispatch_group_t dg, dispatch_queue_t dq,
        dispatch_continuation_t dc, dispatch_qos_t qos)
{
    dispatch_group_enter(dg);
    dc->dc_data = dg;
    _dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}

static inline void
_dispatch_continuation_with_group_invoke(dispatch_continuation_t dc)
{
    struct dispatch_object_s *dou = dc->dc_data;
    unsigned long type = dx_type(dou);
    if (type == DISPATCH_GROUP_TYPE) {
        _dispatch_client_callout(dc->dc_ctxt, dc->dc_func);
        _dispatch_trace_item_complete(dc);
        dispatch_group_leave((dispatch_group_t)dou);
    } else {
        DISPATCH_INTERNAL_CRASH(dx_type(dou), "Unexpected object type");
    }
}

該文章為記錄本人的學(xué)習(xí)路程媳搪,也希望能夠幫助大家铭段,知識(shí)共享,共同成長(zhǎng)秦爆,共同進(jìn)步P蛴蕖!等限!文章地址:http://www.reibang.com/p/07a62a14e258

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末爸吮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子望门,更是在濱河造成了極大的恐慌形娇,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件筹误,死亡現(xiàn)場(chǎng)離奇詭異桐早,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門哄酝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)友存,“玉大人,你說(shuō)我怎么就攤上這事陶衅÷帕ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵搀军,是天一觀的道長(zhǎng)侠驯。 經(jīng)常有香客問我,道長(zhǎng)奕巍,這世上最難降的妖魔是什么吟策? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮的止,結(jié)果婚禮上檩坚,老公的妹妹穿的比我還像新娘。我一直安慰自己诅福,他們只是感情好匾委,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著氓润,像睡著了一般赂乐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咖气,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天挨措,我揣著相機(jī)與錄音,去河邊找鬼崩溪。 笑死浅役,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伶唯。 我是一名探鬼主播觉既,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼乳幸!你這毒婦竟也來(lái)了瞪讼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤粹断,失蹤者是張志新(化名)和其女友劉穎符欠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姿染,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡背亥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年秒际,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狡汉。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡娄徊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盾戴,到底是詐尸還是另有隱情寄锐,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布尖啡,位于F島的核電站橄仆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏衅斩。R本人自食惡果不足惜盆顾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望畏梆。 院中可真熱鬧您宪,春花似錦、人聲如沸奠涌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)溜畅。三九已至捏卓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間慈格,已是汗流浹背怠晴。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留峦椰,地道東北人龄寞。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像汤功,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子溜哮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344