GCD的隊列和線程的關系-GCD源碼學習筆記

什么是GCD

答案參考官方文檔毡代。

什么是隊列

隊列是一種特殊的線性表阅羹,特殊之處在于它只允許在表的前端(front)進行刪除操作,而在表的后端(rear)進行插入操作教寂,和棧一樣捏鱼,隊列是一種操作受限制的線性表。進行插入操作的端稱為隊尾酪耕,進行刪除操作的端稱為隊頭导梆。

GCD中的隊列

隊列的分類

隊列總的來說可以分為串行隊列和并行隊列兩種,但是在iOS中的GCD由于主隊列和全局隊列的特殊性迂烁,我們需要單獨討論看尼,在這里我們把GCD中的隊列細分為四種隊列:串行隊列、并行隊列盟步、主隊列和全局并行隊列藏斩。示例:

// 串行隊列
    dispatch_queue_t serial = dispatch_queue_create("com.dongjianxiong", DISPATCH_QUEUE_SERIAL);
    //并行隊列
    dispatch_queue_t conque = dispatch_queue_create("com.dongjianxiong", DISPATCH_QUEUE_CONCURRENT);
    // 主隊列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    //全局并行隊列
    dispatch_queue_t globQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  • 串行隊列串行隊列中,提交到隊列里的block是按循序執(zhí)行的却盘,而且是一個執(zhí)行完才能執(zhí)行下一個狰域。串行隊列可以通dispatch_queue_create()函數(shù)創(chuàng)建,第二個參數(shù)可以是DISPATCH_QUEUE_SERIAL或者NULL:
dispatch_queue_t serial = dispatch_queue_create("com.dongjianxiong", DISPATCH_QUEUE_SERIAL);
  • 并行隊列隊列里中的block可以同時調(diào)用多個黄橘,不需要等到前面的block執(zhí)行完兆览。并行隊列可以通dispatch_queue_create()函數(shù)創(chuàng)建,第二個參數(shù)可以是DISPATCH_QUEUE_CONCURRENT:
dispatch_queue_t conque = dispatch_queue_create("com.dongjianxiong", DISPATCH_QUEUE_CONCURRENT);

并行隊列的創(chuàng)建流程跟串行隊列是一樣的旬陡,不同前面已經(jīng)提到過拓颓,主要是隊列的類型和最大并行數(shù)。并行隊列的類型為OS_dispatch_queue_concurrent描孟,最大并行數(shù)為DISPATCH_QUEUE_WIDTH_MAX驶睦,是個宏定義,定義為:

#define DISPATCH_QUEUE_WIDTH_FULL           0x1000ull
#define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1)
#define DISPATCH_QUEUE_WIDTH_MAX  (DISPATCH_QUEUE_WIDTH_FULL - 2)

DISPATCH_QUEUE_WIDTH_FULL是16進制匿醒,0x1000ull轉換成10進制為4096场航,所以并行隊列的最大并行數(shù)DISPATCH_QUEUE_WIDTH_MAX為4094。

  • 主隊列本身也是個串行隊列廉羔,它的特殊性在于它是和主線程綁定的溉痢。主線程自動創(chuàng)建runloop,主隊列里的block由runloop來調(diào)用。值得注意的一點是孩饼,主隊列只在主線程執(zhí)行台猴,而主線執(zhí)行的不一定是主隊列的。主隊列無法被修改没咙,調(diào)用 dispatch_suspend(), dispatch_resume(), dispatch_set_context()等這些方法操作主線程是無效的赞草。我們可以通過函數(shù)dispatch_get_main_queue獲取:
 dispatch_queue_t mainQueue = dispatch_get_main_queue();
  • 全局并行隊列實際上也是一個并行隊列梯码,它與普通并行隊列相比不一樣的地方是它是由系統(tǒng)創(chuàng)建的宝泵,并且隊列里的block是根據(jù)系統(tǒng)管理的線程池來調(diào)度的執(zhí)行的。全局并行隊列在系統(tǒng)管理的線程池之上提供了優(yōu)先級桶轩娶,系統(tǒng)將根據(jù)需求和負載情況決定為這個線程池分配多少線程儿奶。系統(tǒng)會盡力保持良好的并行水平,當現(xiàn)有工作線程在系統(tǒng)調(diào)用中阻塞時鳄抒,將創(chuàng)建新的線程闯捎。
    全局并行隊列是一個共享資源,因此它需要保證每個使用全局隊列的用戶提交的任務不能無限量提交任務嘁酿,特別是在遇到阻塞時隙券,避免創(chuàng)建大量的線程。
    可以通過函數(shù)dispatch_get_global_queue()獲饶炙尽娱仔;同時它也是不能被修改的,調(diào)用Calls to dispatch_suspend(), dispatch_resume(), dispatch_set_context()等對它進行操作時無效的游桩。
    通過dispatch_get_global_queue()函數(shù)來獲取牲迫。第一個參數(shù)表示優(yōu)先級。這里要說明是借卧,全局隊列它不是像主隊列那樣只有一個盹憎,而是一組隊列。不同的優(yōu)先級隊列是不一樣的铐刘,默認優(yōu)先級是DISPATCH_QUEUE_PRIORITY_DEFAULT陪每,它的值是0,第二個參數(shù)是保留參數(shù)镰吵,正常要傳0檩禾。優(yōu)先級的值都是系統(tǒng)設定好的,目前就給我們開放了幾個優(yōu)先級:
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
隊列dispatch_queue_t的本質
  • dispatch_queue_t是怎么來的
    通過dispatch_queue_create函數(shù)可以創(chuàng)建一個隊列dispatch_queue_t:
dispatch_queue_t queue = dispatch_queue_create("com.dongjianxiong", DISPATCH_QUEUE_SERIAL);

dispatch_queue_t是什么疤祭?怎么來的盼产?跟蹤dispatch_queue_t的聲明,發(fā)現(xiàn)是通過宏定義聲明的:

DISPATCH_DECL(dispatch_queue);
#define DISPATCH_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, dispatch_object)
#define OS_OBJECT_DECL_SUBCLASS(name, super) \
        OS_OBJECT_DECL_IMPL(name, <OS_OBJECT_CLASS(super)>)
#define OS_OBJECT_CLASS(name) OS_##name

#if OS_OBJECT_USE_OBJC
#import <objc/NSObject.h>
#if __has_attribute(objc_independent_class)
#define OS_OBJC_INDEPENDENT_CLASS __attribute__((objc_independent_class))
#endif // __has_attribute(objc_independent_class)
#ifndef OS_OBJC_INDEPENDENT_CLASS
#define OS_OBJC_INDEPENDENT_CLASS

#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

這其中name其實就是dispatch_queue勺馆,最后一行轉化一下大概就是:

typedef NSObject<OS_dispatch_queue>  *dispatch_queue_t戏售;

其實通過編譯后的C++代碼也能得出相同的結果:

// @protocol OS_dispatch_queue <OS_dispatch_object> /* @end */
 typedef NSObject/*<OS_dispatch_queue>*/ * __attribute__((objc_independent_class)) dispatch_queue_t;
// @protocol OS_dispatch_queue_global <OS_dispatch_queue> /* @end */
 typedef NSObject/*<OS_dispatch_queue_global>*/ * __attribute__((objc_independent_class)) dispatch_queue_global_t;
// @protocol OS_dispatch_queue_serial <OS_dispatch_queue> /* @end */
 typedef NSObject/*<OS_dispatch_queue_serial>*/ * __attribute__((objc_independent_class)) dispatch_queue_serial_t;
// @protocol OS_dispatch_queue_main <OS_dispatch_queue_serial> /* @end */
 typedef NSObject/*<OS_dispatch_queue_main>*/ * __attribute__((objc_independent_class)) dispatch_queue_main_t;
// @protocol OS_dispatch_queue_concurrent <OS_dispatch_queue> /* @end */
 typedef NSObject/*<OS_dispatch_queue_concurrent>*/ * __attribute__((objc_independent_class)) dispatch_queue_concurrent_t;

這里證明了dispatch_queue_t其實就是一個OC對象侨核,它的類是OS_dispatch_queue,dispatch_queue_t是OS_dispatch_queue的實現(xiàn)灌灾。
在我們不只看到dispatch_queue_t 的由來搓译,還有它的四個子類。其實這里可以看出OS_dispatch_queue實際上只是一個抽象類紧卒,我們使用的是它的具體的四個子類侥衬,這四個子類的實現(xiàn)分別是dispatch_queue_serial_t(串行隊列)、dispatch_queue_concurrent_t(并行隊列)跑芳、dispatch_queue_main_t(主隊列)和 dispatch_queue_global_t(全局并行隊列)。

  • 隊列的繼承關系
    這里以串行隊列為例直颅,打印它們繼承鏈:
 id queueClass = object_getClass(serial);
    while (queueClass != nil) {
        NSLog(@"%@",queueClass);
        queueClass = [queueClass superclass];
    }

打印結果:

2021-08-22 12:24:12.708284+0800 GCDDemo[40888:22219369] OS_dispatch_queue_serial
2021-08-22 12:24:13.977272+0800 GCDDemo[40888:22219369] OS_dispatch_queue
2021-08-22 12:24:15.226658+0800 GCDDemo[40888:22219369] OS_dispatch_object
2021-08-22 12:24:16.567031+0800 GCDDemo[40888:22219369] OS_object
2021-08-22 12:24:17.128303+0800 GCDDemo[40888:22219369] NSObject

這里可以看出隊列的基類也是NSObject博个。事實上所有GCD相關的對象基類都是NSObject。

  • 隊列的底層結構
    GCD對象雖然是OC對象功偿,但是它的底層結構體并不是直接使用NSObject盆佣,它有自己底層結構_os_object_s結構體:
typedef struct _os_object_s {
    _OS_OBJECT_HEADER(
    const _os_object_vtable_s *os_obj_isa,
    os_obj_ref_cnt,
    os_obj_xref_cnt);
} _os_object_s;

typedef struct _os_object_s *_os_object_t;

它也有isa指針os_obj_isa,也有對應的類械荷,在os_obj_isa中:

typedef struct _os_object_vtable_s {
    _OS_OBJECT_CLASS_HEADER();
} _os_object_vtable_s;
//
static const _os_object_vtable_s _os_object_vtable;

同時它有自己的對象創(chuàng)建共耍、內(nèi)存管理等相關的方法:

_os_object_t
_os_object_alloc(const void *cls, size_t size);

_os_object_t
_os_object_alloc_realized(const void *cls, size_t size);

void _os_object_dealloc(_os_object_t object);

_os_object_t
_os_object_retain(_os_object_t object);

_os_object_t
_os_object_retain_with_resurrect(_os_object_t obj);

void
_os_object_release(_os_object_t object);

隊列的底層結構也是以_os_object_s模板創(chuàng)建的。

隊列的創(chuàng)建

通過查看dispatch_queue_create()函數(shù)源碼了解隊列創(chuàng)建過程吨瞎。隊列創(chuàng)建主要是在dispatch_lane_create_with_target()函數(shù)中實現(xiàn)的痹兜。它主要做一下幾件事情:

  • 根據(jù)是DISPATCH_QUEUE_SERIAL或者DISPATCH_QUEUE_CONCURRENT創(chuàng)建一個dispatch_queue_attr_info_t結構體:
// 創(chuàng)建dqai,dqa表示DISPATCH_QUEUE_SERIAL或者DISPATCH_QUEUE_CONCURRENT
    dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

//dqai的結構體定義
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;
  • 確定對象類型
    根據(jù)之前創(chuàng)建的dqai確定類型颤诀,DISPATCH_VTABLE宏定義中課獲取它的類字旭,這串行隊列的類為OS_dispatch_queue_serial,而并行的為OS_dispatch_queue_concurrent:
    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);
    }

通過DISPATCH_VTABLE拿到vtable(類信息):

#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
  • 隊列的alloc
    根據(jù)不同的類創(chuàng)建不同的隊列queue:
 // alloc
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s));
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;
    return obj;
}

實際上創(chuàng)建一個_os_object_t結構體崖叫,并初始化isa指針遗淳,指向對應的類。

  • 隊列init
// init
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0)); 

初始化隊列時會根據(jù)dqai.dqai_concurrent 判斷是串行隊列還是并行隊列心傀。這里是串行隊列屈暗,所以最大并行數(shù)為1,如果是并行隊列脂男,那么最大并行數(shù)為DISPATCH_QUEUE_WIDTH_MAX:

#define DISPATCH_QUEUE_WIDTH_FULL           0x1000ull
#define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1)
#define DISPATCH_QUEUE_WIDTH_MAX (DISPATCH_QUEUE_WIDTH_FULL - 2)

DISPATCH_QUEUE_WIDTH_FULL的值為0x1000ull养叛,16進制,轉化成10進制就是4096疆液,那DISPATCH_QUEUE_WIDTH_MAX=4096-2=4094一铅,最大并行數(shù)4094。DISPATCH_QUEUE_WIDTH_POOL是全局并行隊列的堕油,它比普通并行隊列的最大并行數(shù)大潘飘。

  • 創(chuàng)建并初始化隊列完成之后設置label和優(yōu)先級等其他信息肮之,之后包裝一下然后就返回了:
    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;
      return _dispatch_trace_queue_create(dq)._dq;
  • 主隊列的創(chuàng)建
    主隊列的是在程序啟動的時候就被系統(tǒng)主動創(chuàng)建的,具體是在libdispatch_init方法里面被創(chuàng)建的卜录,在main函數(shù)調(diào)用之前就創(chuàng)建了(點擊了解iOS程序啟動流程):
          //設置隊列
    _dispatch_queue_set_current(&_dispatch_main_q);
        //綁定主線程
    _dispatch_queue_set_bound_thread(&_dispatch_main_q);

_dispatch_main_q是主隊列的底層結構戈擒,它的結構體_dispatch_main_q數(shù)據(jù)結構如下:

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

這里我們可以看到主線程的最大并行數(shù)width為1,也就是串行艰毒。而且主隊列的序號dq_serialnum為1筐高。

  • 全局并行隊列的創(chuàng)建
    全局隊列也是由系統(tǒng)創(chuàng)建的,它是一個集合丑瞧。我們只能通過dispatch_get_global_queue()函數(shù)獲取柑土,而且只能獲取對應的優(yōu)先級的,如果我們隨便傳一個優(yōu)先級绊汹,獲取的queue就有可能為空稽屏。以下是demo演示:
dispatch_queue_t globQueue0 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t globQueue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    dispatch_queue_t globQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_queue_t globQueue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    dispatch_queue_t customGlobQueue = dispatch_get_global_queue(1, 0);
    NSLog(@"\n-globQueue0:%@\n-globQueue1:%@\n-globQueue2:%@\n-globQueue3:%@\n-customGlobQueue:%@",globQueue0,globQueue1,globQueue2,globQueue3,customGlobQueue);

demo打印結果是:

-globQueue0:<OS_dispatch_queue_global: com.apple.root.default-qos>
-globQueue1:<OS_dispatch_queue_global: com.apple.root.utility-qos>
-globQueue2:<OS_dispatch_queue_global: com.apple.root.user-initiated-qos>
-globQueue3:<OS_dispatch_queue_global: com.apple.root.background-qos>
-customGlobQueue:(null)

當我們傳入優(yōu)先級設置為1時,這時候它就找不到對應的隊列西乖,說明沒有優(yōu)先級為1的隊列狐榔,所以這個值還是不要隨便傳。我們平時用的最多的是默認default=0優(yōu)先級的隊列获雕,它的label是com.apple.root.default-qos薄腻,通過這個label我們到libdispatch庫里面搜索源碼:

struct dispatch_queue_global_s _dispatch_root_queues[] = {
#define _DISPATCH_ROOT_QUEUE_IDX(n, flags) \
        ((flags & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) ? \
        DISPATCH_ROOT_QUEUE_IDX_##n##_QOS_OVERCOMMIT : \
        DISPATCH_ROOT_QUEUE_IDX_##n##_QOS)
#define _DISPATCH_ROOT_QUEUE_ENTRY(n, flags, ...) \
    [_DISPATCH_ROOT_QUEUE_IDX(n, flags)] = { \
        DISPATCH_GLOBAL_OBJECT_HEADER(queue_global), \
        .dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, \
        .do_ctxt = _dispatch_root_queue_ctxt(_DISPATCH_ROOT_QUEUE_IDX(n, flags)), \
        .dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL), \
        .dq_priority = flags | ((flags & DISPATCH_PRIORITY_FLAG_FALLBACK) ? \
                _dispatch_priority_make_fallback(DISPATCH_QOS_##n) : \
                _dispatch_priority_make(DISPATCH_QOS_##n, 0)), \
        __VA_ARGS__ \
    }
    _DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, 0,
        .dq_label = "com.apple.root.maintenance-qos",
        .dq_serialnum = 4,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.maintenance-qos.overcommit",
        .dq_serialnum = 5,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, 0,
        .dq_label = "com.apple.root.background-qos",
        .dq_serialnum = 6,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.background-qos.overcommit",
        .dq_serialnum = 7,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, 0,
        .dq_label = "com.apple.root.utility-qos",
        .dq_serialnum = 8,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.utility-qos.overcommit",
        .dq_serialnum = 9,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
        .dq_label = "com.apple.root.default-qos",
        .dq_serialnum = 10,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,
            DISPATCH_PRIORITY_FLAG_FALLBACK | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.default-qos.overcommit",
        .dq_serialnum = 11,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, 0,
        .dq_label = "com.apple.root.user-initiated-qos",
        .dq_serialnum = 12,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.user-initiated-qos.overcommit",
        .dq_serialnum = 13,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, 0,
        .dq_label = "com.apple.root.user-interactive-qos",
        .dq_serialnum = 14,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.user-interactive-qos.overcommit",
        .dq_serialnum = 15,
    ),
};

這里可以看出,全局并行隊列是一個集合届案。它們都是由系統(tǒng)創(chuàng)建的庵楷,它是個單利,所以要用dispatch_once_f函數(shù)來創(chuàng)建萝玷。:

static inline void
_dispatch_root_queues_init(void)
{
    dispatch_once_f(&_dispatch_root_queues_pred, NULL,
            _dispatch_root_queues_init_once);
}

在函數(shù)_dispatch_root_queues_init_once中進行初始化:

static void
_dispatch_root_queues_init_once(void *context DISPATCH_UNUSED)
{
    _dispatch_fork_becomes_unsafe();
#if DISPATCH_USE_INTERNAL_WORKQUEUE
    size_t i;
    for (i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) {
        _dispatch_root_queue_init_pthread_pool(&_dispatch_root_queues[i], 0,
                _dispatch_root_queues[i].dq_priority);
    }
#else
    int wq_supported = _pthread_workqueue_supported();
    int r = ENOTSUP;

    if (!(wq_supported & WORKQ_FEATURE_MAINTENANCE)) {
        DISPATCH_INTERNAL_CRASH(wq_supported,
                "QoS Maintenance support required");
    }

#if DISPATCH_USE_KEVENT_SETUP
    struct pthread_workqueue_config cfg = {
        .version = PTHREAD_WORKQUEUE_CONFIG_VERSION,
        .flags = 0,
        .workq_cb = 0,
        .kevent_cb = 0,
        .workloop_cb = 0,
        .queue_serialno_offs = dispatch_queue_offsets.dqo_serialnum,
#if PTHREAD_WORKQUEUE_CONFIG_VERSION >= 2
        .queue_label_offs = dispatch_queue_offsets.dqo_label,
#endif
    };
#endif

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
    if (unlikely(!_dispatch_kevent_workqueue_enabled)) {
#if DISPATCH_USE_KEVENT_SETUP
        cfg.workq_cb = _dispatch_worker_thread2;
        r = pthread_workqueue_setup(&cfg, sizeof(cfg));
#else
        r = _pthread_workqueue_init(_dispatch_worker_thread2,
                offsetof(struct dispatch_queue_s, dq_serialnum), 0);
#endif // DISPATCH_USE_KEVENT_SETUP
#if DISPATCH_USE_KEVENT_WORKLOOP
    } else if (wq_supported & WORKQ_FEATURE_WORKLOOP) {
#if DISPATCH_USE_KEVENT_SETUP
        cfg.workq_cb = _dispatch_worker_thread2;
        cfg.kevent_cb = (pthread_workqueue_function_kevent_t) _dispatch_kevent_worker_thread;
        cfg.workloop_cb = (pthread_workqueue_function_workloop_t) _dispatch_workloop_worker_thread;
        r = pthread_workqueue_setup(&cfg, sizeof(cfg));
#else
        r = _pthread_workqueue_init_with_workloop(_dispatch_worker_thread2,
                (pthread_workqueue_function_kevent_t)
                _dispatch_kevent_worker_thread,
                (pthread_workqueue_function_workloop_t)
                _dispatch_workloop_worker_thread,
                offsetof(struct dispatch_queue_s, dq_serialnum), 0);
#endif // DISPATCH_USE_KEVENT_SETUP
#endif // DISPATCH_USE_KEVENT_WORKLOOP
#if DISPATCH_USE_KEVENT_WORKQUEUE
    } else if (wq_supported & WORKQ_FEATURE_KEVENT) {
#if DISPATCH_USE_KEVENT_SETUP
        cfg.workq_cb = _dispatch_worker_thread2;
        cfg.kevent_cb = (pthread_workqueue_function_kevent_t) _dispatch_kevent_worker_thread;
        r = pthread_workqueue_setup(&cfg, sizeof(cfg));
#else
        r = _pthread_workqueue_init_with_kevent(_dispatch_worker_thread2,
                (pthread_workqueue_function_kevent_t)
                _dispatch_kevent_worker_thread,
                offsetof(struct dispatch_queue_s, dq_serialnum), 0);
#endif // DISPATCH_USE_KEVENT_SETUP
#endif
    } else {
        DISPATCH_INTERNAL_CRASH(wq_supported, "Missing Kevent WORKQ support");
    }
#pragma clang diagnostic pop

    if (r != 0) {
        DISPATCH_INTERNAL_CRASH((r << 16) | wq_supported,
                "Root queue initialization failed");
    }
#endif // DISPATCH_USE_INTERNAL_WORKQUEUE
}

同步嫁乘、異步

在iOS中,隊列的調(diào)度主要由兩個函數(shù)dispatch_async()(異步)和dispatch_sync()(同步)球碉,它們負責將block添加到對應的隊列中蜓斧,然后在適當?shù)臅r候回調(diào)block。

異步函數(shù)-dispatch_async

異步函數(shù)dispatch_async的作用是可以異步添加block到派發(fā)隊列中睁冬。簡單來說就是調(diào)用dispatch_async不會阻塞當前線程挎春,block添加到隊列中之后立即返回,不需要等到block執(zhí)行完成豆拨。使用示例:

dispatch_async(mainQueue, ^{
       NSLog(@"1");
   });
  • 底層原理
    通過底層源碼來分析dispatch_async的實現(xià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);
}

創(chuàng)建一個任務包裝器直奋,用于接收并保存當前的任務塊block,通過函數(shù)式保存下來施禾,等到合適的時機調(diào)用:

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)
{
    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;
        // will initialize all fields but requires dc_flags & dc_ctxt to be set
        return _dispatch_continuation_init_slow(dc, dqu, flags);
    }
    dispatch_function_t func = _dispatch_Block_invoke(work);
    if (dc_flags & DC_FLAG_CONSUME) {
        func = _dispatch_call_block_and_release;
    }
    return _dispatch_continuation_init_f(dc, dqu, ctxt, func, flags, dc_flags);
}

然后把任務包裝器添加到隊列中去脚线,然后立即返回:

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

這里dx_push會把block添加到對應的隊列之中,后面的流程 _dispatch_continuation_async -> dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)弥搞,這里dq_push是一個函數(shù)指針:

#define DISPATCH_QUEUE_VTABLE_HEADER(x); \
    DISPATCH_OBJECT_VTABLE_HEADER(x); \
    void (*const dq_activate)(dispatch_queue_class_t); \
    void (*const dq_wakeup)(dispatch_queue_class_t, dispatch_qos_t, \
            dispatch_wakeup_flags_t); \
    void (*const dq_push)(dispatch_queue_class_t, dispatch_object_t, \
            dispatch_qos_t)

void (*const dq_push)是個函數(shù)指針邮绿,queue子類的實現(xiàn)不一樣渠旁,根據(jù)不同的子類函數(shù)實現(xiàn)不一樣。以下是幾個常用的queue的dq_push實現(xiàn)(源碼具體在init.c文件中):

DISPATCH_NOINLINE
static void
_dispatch_object_no_invoke(dispatch_object_t dou,
        DISPATCH_UNUSED dispatch_invoke_context_t dic,
        DISPATCH_UNUSED dispatch_invoke_flags_t flags)
{
    DISPATCH_INTERNAL_CRASH(dx_type(dou._do), "do_invoke called");
}

/*
 * Dispatch queue cluster
 */

DISPATCH_NOINLINE
static void
_dispatch_queue_no_activate(dispatch_queue_class_t dqu)
{
    DISPATCH_INTERNAL_CRASH(dx_type(dqu._dq), "dq_activate called");
}

DISPATCH_VTABLE_INSTANCE(queue,
    // This is the base class for queues, no objects of this type are made
    .do_type        = _DISPATCH_QUEUE_CLUSTER,
    .do_dispose     = _dispatch_object_no_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_object_no_invoke,
    .dq_activate    = _dispatch_queue_no_activate,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_serial, lane,
    .do_type        = DISPATCH_QUEUE_SERIAL_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_lane_activate,
    .dq_wakeup      = _dispatch_lane_wakeup,
    .dq_push        = _dispatch_lane_push,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_concurrent, lane,
    .do_type        = DISPATCH_QUEUE_CONCURRENT_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_lane_activate,
    .dq_wakeup      = _dispatch_lane_wakeup,
    .dq_push        = _dispatch_lane_concurrent_push,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_global, lane,
    .do_type        = DISPATCH_QUEUE_GLOBAL_ROOT_TYPE,
    .do_dispose     = _dispatch_object_no_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_object_no_invoke,

    .dq_activate    = _dispatch_queue_no_activate,
    .dq_wakeup      = _dispatch_root_queue_wakeup,
    .dq_push        = _dispatch_root_queue_push,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_main, lane,
    .do_type        = DISPATCH_QUEUE_MAIN_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_queue_no_activate,
    .dq_wakeup      = _dispatch_main_queue_wakeup,
    .dq_push        = _dispatch_main_queue_push,
);

這里我們看到dq_push函數(shù)每個子類是不一樣的船逮。后面會分別分析顾腊。
備注:異步函數(shù)具有開啟新的線程的能力,但是不一定會開啟新的線程挖胃,比如異步函數(shù)執(zhí)行主隊列:

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

上面的demo時不會開啟新的線程的杂靶,因為主隊列只能在主線程執(zhí)行。

同步函數(shù)-dispatch_sync

同步函數(shù)dispatch_sync的作用是可以同步添加block到派發(fā)隊列中酱鸭。簡單來說就是調(diào)用dispatch_sync會阻塞當前線程吗垮,block添加到隊列中之后不會立即返回,而是需要等到block執(zhí)行完成才能返回凹髓,并繼續(xù)執(zhí)行后面的代碼抱既。

  • 使用示例:
dispatch_async(queue, ^{
       NSLog(@"1");
   });
  • 調(diào)用流程
    dispatch_sync->_dispatch_sync_f -> _dispatch_sync_f_inline->
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)));
}

到這里的話會根據(jù)不同的隊列類型進行調(diào)用,比如說會區(qū)分是串行隊列扁誓、或者是并行隊列。這個在后面的同步串行和同步并行部分繼續(xù)分析蚀之。
備注:同步會阻塞當前線程蝗敢,但不一定在當前線程執(zhí)行隊列任務,比如下面的代碼:

dispatch_queue_t conque1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    NSLog(@"1");
    dispatch_async(conque1, ^{
        NSLog(@"2");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"3");
        });
    });
    NSLog(@"4");

上面代碼中足删,在子線程同步執(zhí)行主隊列寿谴,主隊列會交給主線線程執(zhí)行,執(zhí)行完block之后再回到當前子線程執(zhí)行下面的代碼失受。

隊列讶泰、函數(shù)和線程的關系

有了隊列串行和并行,函數(shù)的同步異步拂到,我們就可以把它們之間的使用組合總結為同步串行痪署、同步并行、異步串行和異步并行兄旬。但是由于前面說過的主隊列和全局并行隊列的特殊性狼犯,在使用上還要細分,接下來一個個分析领铐。

同步串行

代碼示例:

- (void)serialSyncTest{
    //創(chuàng)建串行隊列
    dispatch_queue_t queue = dispatch_queue_create("com.hello-world.dongjianxiong", DISPATCH_QUEUE_SERIAL);
   
    NSLog(@"1");
    dispatch_sync(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        NSLog(@"2");
    });
    NSLog(@"3");
}

這段代碼的執(zhí)行流程是1 -> 2 -> 3悯森。
在這里dispatch_sync()調(diào)用流程是:

dispatch_sync -> _dispatch_sync_f -> _dispatch_sync_f_inline -> _dispatch_barrier_sync_f->_dispatch_barrier_sync_f_inline -> _dispatch_lane_barrier_sync_invoke_and_complete -> _dispatch_sync_function_invoke_inline:

  • 在函數(shù)_dispatch_sync_f_inline()中判斷是否是串行隊列,這里是同步串行绪撵,所以走_dispatch_barrier_sync_f
if (likely(dq->dq_width == 1)) {
        return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags);
    }
  • 函數(shù)_dispatch_barrier_sync_f_inline這里會嘗試獲取同步鎖瓢姻,判斷是繼續(xù)執(zhí)行,還是走_dispatch_sync_f_slow流程:
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)));
}

函數(shù)中嘗試獲得鎖(dispatch_queue_try_acquire_barrier_sync),如果獲取成功音诈,進入_dispatch_introspection_sync_begin(dl)對隊列進行調(diào)整幻碱,比如根據(jù)優(yōu)先級調(diào)整循序等绎狭。然后進行block調(diào)用:
_dispatch_lane_barrier_sync_invoke_and_complete -> _dispatch_sync_function_invoke_inline:

DISPATCH_ALWAYS_INLINE
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ù)_dispatch_sync_function_invoke_inline()中進行push隊列,然后在當前線程中調(diào)用block(_dispatch_client_callout)收班,調(diào)用完成之后然后pop出列坟岔。

  • 死鎖的原因
    如果前面獲取同步鎖失敗,則會進入_dispatch_sync_f_slow->DISPATCH_WAIT_FOR_QUEUE流程摔桦,在這里等待鎖的釋放社付。在這里會校驗當前的隊列和要同步的隊列是否同一個,如果是就拋出死鎖崩潰信息:
uint64_t dq_state = _dispatch_wait_prepare(dq);
    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");
    }
  • block調(diào)用
    串行隊列:_dispatch_lane_barrier_sync_invoke_and_complete->_dispatch_client_callout
    主隊列由runloop調(diào)用:
    __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__->_dispatch_main_queue_callback_4CF->_dispatch_client_callout->_dispatch_async_and_wait_invoke->_dispatch_client_callout
同步并行

代碼示例:

- (void)concurrentSyncTest{
    //創(chuàng)建并行隊列
    dispatch_queue_t queue = dispatch_queue_create("com.hello-world.dongjianxiong", DISPATCH_QUEUE_CONCURRENT);
   NSLog(@"1");
   dispatch_sync(queue, ^{
            NSLog(@"%d-%@",i,[NSThread currentThread]);
           NSLog(@"2");
        });
    NSLog(@"3");
}

代碼執(zhí)行順序:1->2->3邻耕,與同步串行一樣鸥咖。

  • 普通隊列同步并行dispatch_sync()調(diào)用流程是:

dispatch_sync -> _dispatch_sync_f ->_dispatch_sync_f_inline ->_dispatch_sync_invoke_and_complete -> _dispatch_sync_function_invoke_inline

  • 全局隊列同步并行dispatch_sync()調(diào)用流程是
    代碼示例:
- (void)globalSyncTest{
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    NSLog(@"1");
    dispatch_sync(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        NSLog(@"2");
    });
    NSLog(@"3");
}

dispatch_sync -> _dispatch_sync_f->_dispatch_sync_f_inline->_dispatch_sync_f_slow ->__DISPATCH_WAIT_FOR_QUEUE__ -> dx_push
globalQueue會交由dx_push添加到隊列中,然后等待系統(tǒng)線程池分配線程執(zhí)行block兄世。

  • block 調(diào)用
    同步并行:_dispatch_sync_invoke_and_complete->_dispatch_client_callout
    全局同步并行:_dispatch_sync_function_invoke->_dispatch_client_callout
異步串行

代碼示例:

- (void)serialAsyncTest{
    //創(chuàng)建串行隊列
    dispatch_queue_t queue = dispatch_queue_create("com.hello-world.dongjianxiong", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        NSLog(@"2");
    });
    NSLog(@"3");
}

代碼執(zhí)行順序:1->3->2啼辣。這里的block不會阻塞3的執(zhí)行,它可能在新的線程執(zhí)行的御滩,也可能不是鸥拧。

dispatch_async()調(diào)用流程:

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

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_serial, lane,
    .do_type        = DISPATCH_QUEUE_SERIAL_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_lane_activate,
    .dq_wakeup      = _dispatch_lane_wakeup,
    .dq_push        = _dispatch_lane_push,
);
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_main, lane,
    .do_type        = DISPATCH_QUEUE_MAIN_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_queue_no_activate,
    .dq_wakeup      = _dispatch_main_queue_wakeup,
    .dq_push        = _dispatch_main_queue_push,
);

普通串行隊列_dispatch_lane_push(dq_push)->dx_wakeup(x, y, z) dx_vtable(x)->_dispatch_lane_wakeup(dq_wakeup) ->_dispatch_queue_wakeup->
主隊列:_dispatch_lane_push(dq_push)->dx_wakeup(x, y, z) dx_vtable(x)->_dispatch_main_queue_wakeup(dq_wakeup)->_dispatch_runloop_queue_wakeup`

void
_dispatch_main_queue_wakeup(dispatch_queue_main_t dq, dispatch_qos_t qos,
        dispatch_wakeup_flags_t flags)
{
#if DISPATCH_COCOA_COMPAT
    if (_dispatch_queue_is_thread_bound(dq)) {
        return _dispatch_runloop_queue_wakeup(dq->_as_dl, qos, flags);
    }
#endif
    return _dispatch_lane_wakeup(dq, qos, flags);
}

由于全局隊列綁定到主線程,所以任務會被添加到runloop中削解,等待runloop調(diào)用富弦。

  • block調(diào)用
    串行隊列:_dispatch_workloop_worker_thread->_dispatch_lane_invoke->_dispatch_lane_serial_drain->_dispatch_client_callout->_dispatch_call_block_and_release
    主隊列由runloop調(diào)用:
    __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__->_dispatch_main_queue_callback_4CF->_dispatch_client_callout->_dispatch_call_block_and_release
異步并行

代碼示例:

- (void)concurrentAsyncTest{
    //創(chuàng)建并行隊列
//    dispatch_queue_t queue = dispatch_queue_create("com.hello-world.dongjianxiong", DISPATCH_QUEUE_CONCURRENT);
    //創(chuàng)建全局并行隊列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2");
        NSLog(@"%@",[NSThread currentThread]);
    });
    NSLog(@"3");
}

代碼執(zhí)行流程:1->3->2。這里的block不會阻塞3的執(zhí)行氛驮,它可能在新的線程執(zhí)行的腕柜,也可能不是。

dispatch_async()調(diào)用流程:

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

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_concurrent, lane,
    .do_type        = DISPATCH_QUEUE_CONCURRENT_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_lane_activate,
    .dq_wakeup      = _dispatch_lane_wakeup,
    .dq_push        = _dispatch_lane_concurrent_push,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_global, lane,
    .do_type        = DISPATCH_QUEUE_GLOBAL_ROOT_TYPE,
    .do_dispose     = _dispatch_object_no_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_object_no_invoke,

    .dq_activate    = _dispatch_queue_no_activate,
    .dq_wakeup      = _dispatch_root_queue_wakeup,
    .dq_push        = _dispatch_root_queue_push,
);

對于全局并行隊列矫废,dq_push = _dispatch_root_queue_push盏缤, _dispatch_root_queue_push的流程:

_dispatch_root_queue_push_inline->_dispatch_root_queue_push->_dispatch_root_queue_poke->_dispatch_root_queue_poke_slow

全局并行隊列流程會在_dispatch_root_queue_poke_slow()函數(shù)中選擇一個線程來執(zhí)行當前的block蓖扑,如果線程池的線程不夠用唉铜,則創(chuàng)建新的線程。同時第一次進來會對全局變量進行初始化:

_dispatch_root_queues_init();
  • block 調(diào)用
    并行隊列:_dispatch_worker_thread2->_dispatch_root_queue_drain->_dispatch_async_redirect_invoke->_dispatch_continuation_pop->_dispatch_client_callout->_dispatch_call_block_and_release
    全局并行隊列:_dispatch_worker_thread2->_dispatch_root_queue_drain->_dispatch_queue_override_invoke->_dispatch_client_callout->_dispatch_call_block_and_release赵誓。
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末打毛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子俩功,更是在濱河造成了極大的恐慌幻枉,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诡蜓,死亡現(xiàn)場離奇詭異熬甫,居然都是意外死亡,警方通過查閱死者的電腦和手機蔓罚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門椿肩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瞻颂,“玉大人,你說我怎么就攤上這事郑象」闭猓” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵厂榛,是天一觀的道長盖矫。 經(jīng)常有香客問我,道長击奶,這世上最難降的妖魔是什么辈双? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮柜砾,結果婚禮上湃望,老公的妹妹穿的比我還像新娘。我一直安慰自己痰驱,他們只是感情好证芭,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著担映,像睡著了一般檩帐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上另萤,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音诅挑,去河邊找鬼四敞。 笑死,一個胖子當著我的面吹牛拔妥,可吹牛的內(nèi)容都是我干的忿危。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼没龙,長吁一口氣:“原來是場噩夢啊……” “哼铺厨!你這毒婦竟也來了?” 一聲冷哼從身側響起硬纤,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤解滓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后筝家,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體洼裤,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年溪王,在試婚紗的時候發(fā)現(xiàn)自己被綠了腮鞍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片值骇。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖移国,靈堂內(nèi)的尸體忽然破棺而出吱瘩,到底是詐尸還是另有隱情,我是刑警寧澤迹缀,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布使碾,位于F島的核電站,受9級特大地震影響裹芝,放射性物質發(fā)生泄漏部逮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一嫂易、第九天 我趴在偏房一處隱蔽的房頂上張望兄朋。 院中可真熱鬧,春花似錦怜械、人聲如沸颅和。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽峡扩。三九已至,卻和暖如春障本,著一層夾襖步出監(jiān)牢的瞬間教届,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工驾霜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留案训,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓粪糙,卻偏偏與公主長得像强霎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蓉冈,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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