多線程(二)

我們都知道,程序啟動(dòng)就會(huì)創(chuàng)建一個(gè)主線程來執(zhí)行程序,我們先看一下默認(rèn)開啟的主線程的相關(guān)信息。在main函數(shù)打一個(gè)斷點(diǎn),看函數(shù)調(diào)用棧:

main

可以看到當(dāng)前線程:Queue: com.apple.main-thread(serial),它的名字是com.apple.main-thread,它的類型是serial也就是串行隊(duì)列。
這個(gè)主線程是什么時(shí)候創(chuàng)建又是怎么調(diào)用的呢?我們可以看到在main函數(shù)之前,主線程就已經(jīng)被創(chuàng)建好,猜測(cè)應(yīng)該是dyld鏈接之后,main函數(shù)之前創(chuàng)建的。那我們?cè)趺醋C明呢?這就要去libdispatch源碼中找線索了。

在源碼中找類似init操作看有沒有主線程相關(guān)的初始化。全局搜索dispatch_get_main_queue(,找到下面實(shí)現(xiàn):

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);
}
#define DISPATCH_GLOBAL_OBJECT(type, object) ((type)&(object))

這個(gè)宏定義第一個(gè)參數(shù)type是類型,值是object。也就是說dispatch_queue_main_t是類型,_dispatch_main_q這個(gè)才是真正的值。全局進(jìn)行搜索,發(fā)現(xiàn)結(jié)果有很多,這個(gè)變量要調(diào)用一定會(huì)有賦值的地方,嘗試搜索_dispatch_main_q =,就會(huì)發(fā)現(xiàn)只有一個(gè)地方賦值:

struct dispatch_queue_static_s _dispatch_main_q = {
    DISPATCH_GLOBAL_OBJECT_HEADER(queue_main),
#if !DISPATCH_USE_RESOLVERS
    .do_targetq = _dispatch_get_default_queue(true),
#endif
    .dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |
            DISPATCH_QUEUE_ROLE_BASE_ANON,
    .dq_label = "com.apple.main-thread",
    .dq_atomic_flags = DQF_THREAD_BOUND | DQF_WIDTH(1),
    .dq_serialnum = 1,
};

從上面代碼可以看到,主隊(duì)列的名字com.apple.main-thread,跟我們上面看函數(shù)調(diào)用棧的結(jié)果一樣,也就是沒找錯(cuò)地方。主隊(duì)列的類型DQF_WIDTH(1),串行隊(duì)列,很多人說.dq_serialnum = 1看到這個(gè)num = 1就是主隊(duì)列,其實(shí)不是,這只是隊(duì)列的編號(hào),怎么證明呢?串行隊(duì)列必然相對(duì)并發(fā)隊(duì)列有某些特性,我們找的就是這些特性來證明。

我們創(chuàng)建隊(duì)列,都是用dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t _Nullable attr#>)這個(gè)函數(shù)來創(chuàng)建,現(xiàn)在來研究下創(chuàng)建的底層實(shí)現(xiàn)。

全局搜索dispatch_queue_create(con,為什么要加(con因?yàn)槲覀円宜牡讓訉?shí)現(xiàn),第一個(gè)參數(shù)是const類型,這樣搜索結(jié)果更少方便定位。

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);
}

找到_dispatch_lane_create_with_target的實(shí)現(xiàn),有100多行代碼,我們研究的重點(diǎn)是看它創(chuàng)建的隊(duì)列是什么,也就是返回的是什么隊(duì)列,所以我們先關(guān)注返回值。

返回值是_dispatch_trace_queue_create(dq)._dq,里面trace的含義就是方便追蹤,不是關(guān)注重點(diǎn),重點(diǎn)是里面的dq,我們看dq是如何創(chuàng)建的。從_dispatch_lane_create_with_target方法中找相關(guān)代碼:

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));

第一行是開辟內(nèi)存空間,第二行是構(gòu)造函數(shù)??礃?gòu)造函數(shù)第三個(gè)參數(shù),dqai.dqai_concurrent ? DISPATCH_QUEUE_WIDTH_MAX : 1,命名意思比較明顯,判斷是不是并發(fā)隊(duì)列,如果是傳max,如果不是并發(fā)隊(duì)列(即串行隊(duì)列),參數(shù)就傳1。

我們?cè)倏礃?gòu)造函數(shù)的實(shí)現(xiàn),看這個(gè)參數(shù)是如何使用的:

_dispatch_queue_init

可以看到參數(shù)使用dqf |= DQF_WIDTH(width);,也就是為什么我們上面說DQF_WIDTH(1)是隊(duì)列的類型。
再看下dq_serialnum,它這個(gè)值是根據(jù)os_atomic_inc_orig創(chuàng)建

#define os_atomic_inc_orig(p, m) \
        os_atomic_add_orig((p), 1, m)
#define os_atomic_add_orig(p, v, m) \
        _os_atomic_c11_op_orig((p), (v), m, add, +)
#define _os_atomic_c11_op_orig(p, v, m, o, op) \
        atomic_fetch_##o##_explicit(_os_atomic_c11_atomic(p), v, \
        memory_order_##m)

這個(gè)函數(shù)最后就會(huì)變成atomic_fetch_add_explicit(_os_atomic_c11_atomic(p), 1, memory_order_relaxed),這個(gè)宏一層層封裝最后就是C11標(biāo)準(zhǔn)的函數(shù),atomic_fetch_add_explicit可以理解是把前兩個(gè)參數(shù)想加并返回,_os_atomic_c11_atomic表示是原子操作。

那看下第一個(gè)參數(shù)的值_dispatch_queue_serial_numbers是什么:

// skip zero
// 1 - main_q
// 2 - mgr_q
// 3 - mgr_root_q
// 4,5,6,7,8,9,10,11,12,13,14,15 - global queues
// 17 - workloop_fallback_q
// we use 'xadd' on Intel, so the initial value == next assigned
#define DISPATCH_QUEUE_SERIAL_NUMBER_INIT 17
extern unsigned long volatile _dispatch_queue_serial_numbers;

可以看到1是主隊(duì)列的意思,4-15全局隊(duì)列,我們用dispatch_get_global_queue創(chuàng)建的隊(duì)列為什么沒有2、3在注釋中可以得到解釋。

這里有個(gè)想法,這個(gè)隊(duì)列和線程的num是不是一致的呢?我們創(chuàng)建幾個(gè)線程并打?。?/p>

線程

明顯看到線程num有等于3(不一定一次運(yùn)行就出現(xiàn),我也是運(yùn)行了好多次),也就是線程的num和隊(duì)列的num并不是一個(gè)東西,不要混淆了。

我們分別獲取4種不同的線程打印下線程信息:

線程

去源碼中搜索com.apple.root.default-qos,可以看到全局隊(duì)列對(duì)應(yīng)的num是10

_DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
    .dq_label = "com.apple.root.default-qos",
    .dq_serialnum = 10,
),

我們現(xiàn)在再回到_dispatch_lane_create_with_target這個(gè)方法中,看第一行代碼

dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

這行代碼的意義就是對(duì)線程一些信息進(jìn)行面向?qū)ο蟮姆庋b。再全局搜索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;

一個(gè)用位域表示的結(jié)構(gòu)體。
看下_dispatch_queue_attr_to_info實(shí)現(xiàn),里面有個(gè)小細(xì)節(jié),如果dqa是空的話,就直接返回。

_dispatch_queue_attr_to_info

dispatch_queue_create創(chuàng)建隊(duì)列的時(shí)候,如果attr傳空,通過dqai.dqai_concurrent判斷就會(huì)是串行隊(duì)列,如果attrDISPATCH_QUEUE_SERIAL,可以看到這個(gè)宏的實(shí)現(xiàn)#define DISPATCH_QUEUE_SERIAL NULL也是空,所以他們是等價(jià)的,都是創(chuàng)建串行隊(duì)列。

隊(duì)列的類型

無論我們直接獲取主隊(duì)列或者全局隊(duì)列又或者自己創(chuàng)建的隊(duì)列,最后我們接收的類型都是dispatch_queue_t,點(diǎn)進(jìn)去看這個(gè)類型:DISPATCH_DECL(dispatch_queue);,這里我們可以看到有好幾個(gè)宏定義,他們判斷不同,第一個(gè)#if OS_OBJECT_USE_OBJC,正常會(huì)走到這個(gè)if里面。
再繼續(xù)點(diǎn)就可以看到

#define DISPATCH_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, dispatch_object)

繼續(xù)往下看發(fā)現(xiàn)沒有了,我們只能從源碼中搜索OS_OBJECT_DECL_SUBCLASS,找到三個(gè)宏定義:

#define OS_OBJECT_DECL_SUBCLASS(name, super)  DISPATCH_DECL(name)

這個(gè)相當(dāng)于跟上面DISPATCH_DECL宏定義死循環(huán)了,不是這個(gè)。

#define OS_OBJECT_DECL_SUBCLASS_SWIFT(name, super) \
        OS_EXPORT OS_OBJECT_OBJC_RUNTIME_VISIBLE \
        OS_OBJECT_DECL_IMPL_CLASS(name, OS_OBJECT_CLASS(super))

這個(gè)的條件是#if OS_OBJECT_SWIFT3,也不是這個(gè)。
就剩下一個(gè):

#define OS_OBJECT_DECL_SUBCLASS(name, super) \
        OS_OBJECT_DECL_IMPL(name, <OS_OBJECT_CLASS(super)>)

繼續(xù)往下探索這個(gè)宏定義實(shí)現(xiàn):

#define OS_OBJECT_DECL_IMPL(name, ...) \
        OS_OBJECT_DECL_PROTOCOL(name, __VA_ARGS__) \
        typedef NSObject<OS_OBJECT_CLASS(name)> \
                * OS_OBJC_INDEPENDENT_CLASS name##_t
#define OS_OBJECT_DECL_PROTOCOL(name, ...) \
        @protocol OS_OBJECT_CLASS(name) __VA_ARGS__ \
        @end
#define OS_OBJECT_CLASS(name) OS_##name
  1. DISPATCH_DECL(dispatch_queue);
  2. OS_OBJECT_DECL_SUBCLASS(dispatch_queue, dispatch_object)
  3. OS_OBJECT_DECL_IMPL(dispatch_queue, <OS_OBJECT_CLASS(dispatch_object)>)
  4. OS_OBJECT_DECL_PROTOCOL(dispatch_queue, <OS_OBJECT_CLASS(dispatch_object)>)
    typedef NSObject<OS_OBJECT_CLASS(dispatch_queue)>
    * OS_OBJC_INDEPENDENT_CLASS dispatch_queue_t
  5. @protocol OS_OBJECT_CLASS(dispatch_queue) <OS_OBJECT_CLASS(dispatch_object)>
    @end
    typedef NSObject<OS_OBJECT_CLASS(dispatch_queue)>
    * OS_OBJC_INDEPENDENT_CLASS dispatch_queue_t
  6. @protocol os_dispatch_queue <os_dispatch_object>
    @end
    typedef NSObject<os_dispatch_queue>
    * OS_OBJC_INDEPENDENT_CLASS dispatch_queue_t

也就是dispatch_queue_t本質(zhì)是一個(gè)滿足os_dispatch_queue協(xié)議的對(duì)象。

第二個(gè)#elif defined(__cplusplus) && !defined(__DISPATCH_BUILDING_DISPATCH__),判斷是C++第二個(gè)條件是YES,也就是說底層C++實(shí)現(xiàn)可能會(huì)走到這個(gè)if

#define DISPATCH_DECL(name) \
        typedef struct name##_s : public dispatch_object_s {} *name##_t

typedef struct dispatch_queue_s : public dispatch_object_s {} *dispatch_queue_t

也就是這種情況dispatch_queue_s類型繼承dispatch_object_s的結(jié)構(gòu)體。
在源碼中我們還能看到一個(gè)聯(lián)合體:

typedef union {
    struct _os_object_s *_os_obj;
    struct dispatch_object_s *_do;
    struct dispatch_queue_s *_dq;
    struct dispatch_queue_attr_s *_dqa;
    struct dispatch_group_s *_dg;
    struct dispatch_source_s *_ds;
    struct dispatch_channel_s *_dch;
    struct dispatch_mach_s *_dm;
    struct dispatch_mach_msg_s *_dmsg;
    struct dispatch_semaphore_s *_dsema;
    struct dispatch_data_s *_ddata;
    struct dispatch_io_s *_dchannel;
} dispatch_object_t DISPATCH_TRANSPARENT_UNION;

dispatch_object_t這個(gè)可以是聯(lián)合體里面的各種類型。

之后的條件就沒必要看了,基本不會(huì)走。

它的底層結(jié)構(gòu)因?yàn)榈讓邮荂++,所以我們看第二個(gè),看下dispatch_queue_s結(jié)構(gòu)體的實(shí)現(xiàn):

struct dispatch_queue_s {
    DISPATCH_QUEUE_CLASS_HEADER(queue, void *__dq_opaque1);
    /* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN;
#define DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__) \
    _DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__); \
    /* LP64 global queue cacheline boundary */ \
    unsigned long dq_serialnum; \
    const char *dq_label; \
    DISPATCH_UNION_LE(uint32_t volatile dq_atomic_flags, \
        const uint16_t dq_width, \
        const uint16_t __dq_opaque2 \
    ); \
    dispatch_priority_t dq_priority; \
    union { \
        struct dispatch_queue_specific_head_s *dq_specific_head; \
        struct dispatch_source_refs_s *ds_refs; \
        struct dispatch_timer_source_refs_s *ds_timer_refs; \
        struct dispatch_mach_recv_refs_s *dm_recv_refs; \
        struct dispatch_channel_callbacks_s const *dch_callbacks; \
    }; \
    int volatile dq_sref_cnt
#define _DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__) \
    DISPATCH_OBJECT_HEADER(x); \
    __pointer_sized_field__; \
    DISPATCH_UNION_LE(uint64_t volatile dq_state, \
            dispatch_lock dq_state_lock, \
            uint32_t dq_state_bits \
    )
#endif
#define DISPATCH_OBJECT_HEADER(x) \
    struct dispatch_object_s _as_do[0]; \
    _DISPATCH_OBJECT_HEADER(x)
#define _DISPATCH_OBJECT_HEADER(x) \
    struct _os_object_s _as_os_obj[0]; \
    OS_OBJECT_STRUCT_HEADER(dispatch_##x); \
    struct dispatch_##x##_s *volatile do_next; \
    struct dispatch_queue_s *do_targetq; \
    void *do_ctxt; \
    void *do_finalizer
#define OS_OBJECT_STRUCT_HEADER(x) \
    _OS_OBJECT_HEADER(\
    const struct x##_vtable_s *do_vtable, \
    do_ref_cnt, \
    do_xref_cnt)
#define _OS_OBJECT_HEADER(isa, ref_cnt, xref_cnt) \
        isa; /* must be pointer-sized */ \
        int volatile ref_cnt; \
        int volatile xref_cnt

一層一層宏包裝的繼承鏈。

GCD任務(wù)塊執(zhí)行時(shí)機(jī)

dispatch_sync(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"1");
});

上面代碼在全局并發(fā)隊(duì)列同步執(zhí)行一個(gè)打印,那這個(gè)block塊里面的代碼是什么時(shí)候?qū)崿F(xiàn)的呢?從源碼中找線索:

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);
}

這里重點(diǎn)關(guān)注work即可,就是我們執(zhí)行的block。
搜索_dispatch_Block_invoke可以看到是個(gè)宏定義:

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

執(zhí)行invoke也就是這個(gè)就是work的調(diào)用,繼續(xù)看_dispatch_sync_f是從哪調(diào)用過來的,

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);
}

ctxt就是work,func就是block函數(shù)。

_dispatch_sync_f_inline

我們看到_dispatch_sync_f_inline函數(shù)有好幾個(gè)return,到底走哪個(gè)也不好判斷,這時(shí)候我們就把return對(duì)應(yīng)函數(shù)加到符號(hào)斷點(diǎn)里面,看到底走哪個(gè)。

符號(hào)斷點(diǎn)
image

看到斷點(diǎn)走到了_dispatch_sync_f_slow位置,在源碼中搜索_dispatch_sync_f_slow的實(shí)現(xiàn),里面也是有幾個(gè)return,我們故技重施,繼續(xù)添加符號(hào)斷點(diǎn)并且重新運(yùn)行,

image

看到后面走到的_dispatch_sync_function_invoke這個(gè)方法,

static void
_dispatch_sync_function_invoke(dispatch_queue_class_t dq, void *ctxt,
        dispatch_function_t func)
{
    _dispatch_sync_function_invoke_inline(dq, ctxt, func);
}
static inline void
_dispatch_sync_function_invoke_inline(dispatch_queue_class_t dq, void *ctxt,
        dispatch_function_t func)
{
    dispatch_thread_frame_s dtf;
    _dispatch_thread_frame_push(&dtf, dq);
    _dispatch_client_callout(ctxt, func);
    _dispatch_perfmon_workitem_inc();
    _dispatch_thread_frame_pop(&dtf);
}

參數(shù)ctxt就是之前的work,func就是之前block包裝的函數(shù),跟這個(gè)有關(guān)的就是_dispatch_client_callout(ctxt, func);,全局搜索_dispatch_client_callout(void,有幾個(gè)地方:

static inline void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
    return f(ctxt);
}
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
    @try {
        return f(ctxt);
    }
    @catch (...) {
        objc_terminate();
    }
}
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
    _dispatch_get_tsd_base();
    void *u = _dispatch_get_unwind_tsd();
    if (likely(!u)) return f(ctxt);
    _dispatch_set_unwind_tsd(NULL);
    f(ctxt);
    _dispatch_free_unwind_tsd();
    _dispatch_set_unwind_tsd(u);
}

這兩個(gè)無論調(diào)用哪個(gè),核心思想都是調(diào)用f(ctxt),也就是block中的函數(shù)。

總結(jié)下dispatch_syncblock調(diào)用:

  1. dispatch_sync
  2. _dispatch_sync_f
  3. _dispatch_sync_f_inline
  4. _dispatch_sync_f_slow
  5. _dispatch_sync_function_invoke
  6. _dispatch_sync_function_invoke_inline
  7. _dispatch_client_callout
  8. f(ctxt)

block中打個(gè)斷點(diǎn),看下函數(shù)調(diào)用棧:

image

跟我們分析一致。

接下來我們看下dispatch_asyncblock的調(diào)用時(shí)機(jī)。

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"1");
});

全局搜索dispatch_async(dis

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);
}

我們還是關(guān)注work,找_dispatch_continuation_init的實(shí)現(xiàn)

_dispatch_continuation_init

unlikely不用關(guān)注,再找_dispatch_continuation_init_f實(shí)現(xiàn)

_dispatch_continuation_init_f
_dispatch_continuation_priority_set

也就是說,qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);這行代碼的意義就是把block進(jìn)行對(duì)應(yīng)的封裝以及優(yōu)先級(jí)處理。因?yàn)槭钱惒降?,所以需要?yōu)先級(jí)來進(jìn)行函數(shù)執(zhí)行的參考和依據(jù)。
函數(shù)想要執(zhí)行,肯定就要依賴下面的代碼_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);

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

因?yàn)楹瘮?shù)最后包裝在qos里面,所以我們關(guān)注z這個(gè)參數(shù),全局搜索dq_push

image

全局并發(fā)隊(duì)列是在這賦值,找_dispatch_root_queue_push實(shí)現(xiàn):

_dispatch_root_queue_push

里面第三個(gè)參數(shù)是函數(shù)封裝,并沒有找到相關(guān)的調(diào)用,看最后方法,繼續(xù)找:

DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_root_queue_push_inline(dispatch_queue_global_t dq,
        dispatch_object_t _head, dispatch_object_t _tail, int n)
{
    struct dispatch_object_s *hd = _head._do, *tl = _tail._do;
    if (unlikely(os_mpsc_push_list(os_mpsc(dq, dq_items), hd, tl, do_next))) {
        return _dispatch_root_queue_poke(dq, n, 0);
    }
}
_dispatch_root_queue_poke
_dispatch_root_queue_poke_slow
static inline void
_dispatch_root_queues_init(void)
{
    dispatch_once_f(&_dispatch_root_queues_pred, NULL,
            _dispatch_root_queues_init_once);
}

這里面封裝單利,執(zhí)行一次_dispatch_root_queues_init_once,單利的原理我們?cè)谙旅鏁?huì)分析。

_dispatch_root_queues_init_once

我們先看下函數(shù)調(diào)用棧:


image

函數(shù)調(diào)用是從_dispatch_worker_thread2這個(gè)函數(shù)調(diào)用過來,在方法里面找這個(gè)函數(shù)相關(guān)內(nèi)容:

_pthread_workqueue_init_with_workloop

這個(gè)就是底層pthread的相關(guān)封裝,它是通過workloop來控制是否調(diào)用,這個(gè)workloop又是通過OS控制,受CPU的調(diào)度處理。

死鎖分析

dispatch_queue_t queue = dispatch_queue_create("123", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    NSLog(@"1");
    dispatch_sync(queue, ^{
        NSLog(@"2");
    });
    NSLog(@"3");
});

我們看上面的一段代碼,運(yùn)行就會(huì)發(fā)現(xiàn)崩潰,可以看到崩潰的函數(shù)調(diào)用棧:

死鎖崩潰

死鎖崩潰我們可以看到先走到_dispatch_sync_f_slow函數(shù),然后再走__DISPATCH_WAIT_FOR_QUEUE__發(fā)生崩潰,那現(xiàn)在從源碼分析一下什么情況下回產(chǎn)生死鎖。

我們上面已經(jīng)分析到,dispatch_sync的流程,一開始流程沒有區(qū)別:

  1. dispatch_sync
  2. _dispatch_sync_f
  3. _dispatch_sync_f_inline

走到這都一樣,但是接下來,就不太一樣了,看likely(dq->dq_width == 1)這個(gè)條件,上面分析過dq_width為1時(shí)為串行隊(duì)列,死鎖就是在串行隊(duì)列才會(huì)有,所以我們看這個(gè)if里面的代碼:return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags);

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);
}
_dispatch_barrier_sync_f_inline

因?yàn)槲覀兩厦嬉呀?jīng)看到,死鎖會(huì)走到_dispatch_sync_f_slow函數(shù),所以看這個(gè)方法我們只需要找到這個(gè)函數(shù)的調(diào)用,又回到之前調(diào)用的流程中函數(shù),但是這次判斷就會(huì)不一樣了:

_dispatch_sync_f_slow

為了驗(yàn)證我們分析是正確的,首先運(yùn)行看函數(shù)調(diào)用:

image

再看源碼中__DISPATCH_WAIT_FOR_QUEUE__方法:

__DISPATCH_WAIT_FOR_QUEUE__
#define DISPATCH_CLIENT_CRASH(c, x) do { \
        _dispatch_set_crash_log_cause_and_message((c), \
                "BUG IN CLIENT OF LIBDISPATCH: " x); \
        _dispatch_hardware_crash(); \
    } while (0)

就可以看到源碼分析中輸出的message和我們運(yùn)行匯編message一模一樣,所以分析正確!其實(shí)在messag里面已經(jīng)說明了什么情況會(huì)死鎖,dispatch_sync調(diào)用的隊(duì)列,已經(jīng)是當(dāng)前的線程中了。

流程沒問題了,雖然message我們可以看出端倪,但是我們還是想知道代碼如何判斷會(huì)產(chǎn)生死鎖,也就是說,什么條件下會(huì)走到這些代碼呢。最重要的還是要看__DISPATCH_WAIT_FOR_QUEUE__方法里面的判斷

if (unlikely(_dq_state_drain_locked_by(dq_state, dsc->dsc_waiter))) {
    DISPATCH_CLIENT_CRASH((uintptr_t)dq_state,
            "dispatch_sync called on queue "
            "already owned by current thread");
}

當(dāng)條件滿足時(shí),才會(huì)走到下面的方法,我們現(xiàn)在就分析這個(gè)條件。
dsc->dsc_waiter就是當(dāng)前線程的id,dsc是上面參數(shù)傳過來的,

image
#define _dispatch_tid_self()        ((dispatch_tid)(_dispatch_get_tsd_base()->tid))

dq_state = _dispatch_wait_prepare(dq);代表當(dāng)前隊(duì)列的狀態(tài)。

_dq_state_drain_locked_by這個(gè)函數(shù)實(shí)現(xiàn):

static inline bool
_dq_state_drain_locked_by(uint64_t dq_state, dispatch_tid tid)
{
    return _dispatch_lock_is_locked_by((dispatch_lock)dq_state, tid);
}
static inline bool
_dispatch_lock_is_locked_by(dispatch_lock lock_value, dispatch_tid tid)
{
    // equivalent to _dispatch_lock_owner(lock_value) == tid
    return ((lock_value ^ tid) & DLOCK_OWNER_MASK) == 0;
}

這里面的掩碼DLOCK_OWNER_MASK是很大的值#define DLOCK_OWNER_MASK ((dispatch_lock)0xfffffffc),也就是前面如果是0的話,返回YES就會(huì)造成死鎖,也就是statetid相同時(shí),會(huì)死鎖,也就是說當(dāng)前線程要等待,然后你又調(diào)用當(dāng)前線程執(zhí)行,就會(huì)造成死鎖。

單利原理分析

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    NSLog(@"once");
});

gcd單利寫法如上面代碼,底層是怎么實(shí)現(xiàn)的呢,我們現(xiàn)在來研究一下。
在源碼中全局搜索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
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;
    };
} dispatch_once_gate_s, *dispatch_once_gate_t;

判斷能否進(jìn)入函數(shù)執(zhí)行:

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)用函數(shù)并廣播:

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);
}

流程總結(jié)如下:

  1. 先試類型強(qiáng)制轉(zhuǎn)換成dispatch_once_gate_t,標(biāo)記這個(gè)是否已經(jīng)做過和一些鎖
  2. 判斷dgo_once等于DLOCK_ONCE_DONE,相當(dāng)于做過,直接return
  3. 如果嘗試加鎖失敗,直接把dgo_once設(shè)置成DLOCK_ONCE_DONE
  4. 判斷l沒有被加鎖,也就是沒有別的線程操作,調(diào)用函數(shù)并且廣播
  5. 等待其他人開鎖
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容