一、前言
在iOS開發(fā)過程中,我們知道多線程技術(shù)是使用最多的情況,能快速的執(zhí)行多個(gè)調(diào)度任務(wù)的執(zhí)行。而在多線程開發(fā)過程當(dāng)中,多線程技術(shù)有好幾種,其中包括pthread,NSThread,NSOperation和GCD,而GCD是整個(gè)iOS開發(fā)過程中使用最多的也是最安全的一種技術(shù),因?yàn)镚CD是基于C/C++函數(shù)的封裝實(shí)現(xiàn),因此線程比較安全,在多線程開發(fā)過程中為我們開發(fā)者省去了基于考慮線程安全的事情,專注開發(fā)。是多線程開發(fā)過程中的首選。
然而GCD中
*1 是如何來分配多線程的調(diào)度任務(wù)的?
*2 結(jié)構(gòu)又有哪些?
*3任務(wù)的調(diào)度過程是如何調(diào)度的?
帶著這一系列的問題我們開始探索GCD的函數(shù)與隊(duì)列的搭配使用情況。
二、函數(shù)
在我們測(cè)試的Demo中我們執(zhí)行一個(gè)相關(guān)的GCD同步函數(shù)dispatch_async,同時(shí)向編譯器下一個(gè)符號(hào)斷點(diǎn) 簡單的打印一個(gè)任務(wù)
dispatch_async(conque, ^{
NSLog(@"12334");
});
我們會(huì)看到當(dāng)前的斷點(diǎn)會(huì)定位到系統(tǒng)的libdispatch.dylib dispatch_async:

通過以上我們就知道GCD的源碼在libdispatch.dylib,帶著找到開源的庫。懷著一個(gè)好奇的心去看看具體的GCD相關(guān)函數(shù)是如何實(shí)現(xiàn),底層的調(diào)用機(jī)制又是怎么樣的,接下來讓我們進(jìn)入函數(shù)的探索環(huán)節(jié)吧
2.1 dispatch_sync(同步函數(shù))
我們進(jìn)去到libdispatch.dylib,進(jìn)行全局的搜索dispatch_sync會(huì)找到相應(yīng)的函數(shù)定義
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ào)用了一個(gè)_dispatch_sync_f函數(shù)來實(shí)現(xiàn)同步函數(shù)的實(shí)現(xiàn)的。我們?cè)俅芜M(jìn)入看看當(dāng)前函數(shù)是如何實(shí)現(xiàn)的
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);
}
我們從源碼就能看到當(dāng)前_dispatch_sync_f是通過封裝一個(gè)叫_dispatch_sync_f_inline內(nèi)聯(lián)函數(shù)從而達(dá)到相關(guān)的同步函數(shù)
_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);
}
_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)));
}
再次順著相應(yīng)的開源的代碼進(jìn)入到同步函數(shù)的執(zhí)行以及完成函數(shù)_dispatch_sync_invoke_and_complete
_dispatch_sync_invoke_and_complete(dispatch_lane_t dq, void *ctxt,
dispatch_function_t func DISPATCH_TRACE_ARG(void *dc))
{
_dispatch_sync_function_invoke_inline(dq, ctxt, func);
_dispatch_trace_item_complete(dc);
_dispatch_lane_non_barrier_complete(dq, 0);
}
再次進(jìn)入到_dispatch_sync_function_invoke_inline的我們能看到相應(yīng)的函數(shù)調(diào)用過程
_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);
}
最終到_dispatch_client_callout
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
@try {
return f(ctxt);
}
@catch (...) {
objc_terminate();
}
}
執(zhí)行步驟如下:
- 1 先給任務(wù)分配一個(gè)任務(wù)棧
- 2把當(dāng)前調(diào)度任務(wù)進(jìn)行入棧。讓當(dāng)前線程準(zhǔn)備開始調(diào)度相關(guān)的任務(wù)
- 3 線程調(diào)度當(dāng)前的任務(wù)
- 4 執(zhí)行完成后把當(dāng)前任務(wù)彈棧。進(jìn)行相應(yīng)的釋放操作,
總結(jié):同步函數(shù)的執(zhí)行流程是dispatch_async -> _dispatch_sync_f -> _dispatch_sync_f_inline -> _dispatch_sync_invoke_and_complete -> _dispatch_sync_function_invoke_inline -> _dispatch_client_callout -> f(ctxt);
2.2dispatch_async(異步函數(shù))
再次全局搜索2dispatch_async進(jìn)入到相關(guān)的異步函數(shù)的定義如下:
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;
// 任務(wù)包裝器 - 接受 - 保存 - 函數(shù)式
// 保存 block
qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}
我們通過代碼知道,異步函數(shù)是通過一個(gè)包裝器進(jìn)行相應(yīng)的任務(wù)包裝,然后進(jìn)行相應(yīng)的函數(shù)執(zhí)行:進(jìn)入到_dispatch_continuation_async一探究竟;源碼如下
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);
}
我們知道如果當(dāng)前是GCD的調(diào)用會(huì)走_dispatch_trace_item_push,所以進(jìn)入到源碼一探究竟
_dispatch_trace_item_push(dispatch_queue_class_t dqu, dispatch_object_t _tail)
{
_dispatch_trace_item_push_inline(dqu._dq, _tail._do);
_dispatch_introspection_queue_push(dqu, _tail);
}
順著源碼進(jìn)入第一個(gè)函數(shù)去初探,我們能看到是一個(gè)任務(wù)隊(duì)列封裝器,目的就是通過賦值和相應(yīng)的操作以返回一個(gè)全新的任務(wù)隊(duì)列。有興趣的朋友可以自行去研究了一下;而實(shí)際調(diào)用過程是第二個(gè)函數(shù)_dispatch_introspection_queue_push
static inline void
_dispatch_introspection_queue_push(dispatch_queue_class_t dqu,
dispatch_object_t dou)
{
_dispatch_introspection_queue_item_enqueue(dqu, dou);
}
再次進(jìn)入底層_dispatch_introspection_queue_item_enqueue
_dispatch_introspection_queue_item_enqueue(dispatch_queue_t dq,
dispatch_object_t dou)
{
DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(
queue_item_enqueue, dq, dou);
if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_item_enqueue)) {
_dispatch_introspection_queue_item_enqueue_hook(dq, dou);
}
}
到最終我們看到調(diào)用的關(guān)鍵字段_dispatch_introspection_queue_item_enqueue_hook
_dispatch_introspection_queue_item_enqueue_hook(dispatch_queue_t dq,
dispatch_object_t dou)
{
dispatch_introspection_queue_item_s diqi;
diqi = dispatch_introspection_queue_item_get_info(dq, dou._dc);
dispatch_introspection_hook_callout_queue_item_enqueue(dq, &diqi);
}
我們看到最終異步函數(shù)的調(diào)用是通過封裝的宏定義函數(shù)執(zhí)行的
#define DISPATCH_INTROSPECTION_HOOK_CALLOUT(h, ...) ({ \
__typeof__(_dispatch_introspection_hooks.h) _h; \
_h = _dispatch_introspection_hooks.h; \
if (unlikely((void*)(_h) != DISPATCH_INTROSPECTION_NO_HOOK)) { \
_h(__VA_ARGS__); \
} })
總結(jié):異步函數(shù)的執(zhí)行流程是 dispatch_async -> _dispatch_continuation_async -> _dispatch_trace_item_push -> _dispatch_introspection_queue_push -> _dispatch_introspection_queue_item_enqueue -> _dispatch_introspection_queue_item_enqueue_hook -> DISPATCH_INTROSPECTION_HOOK_CALLOUT
三、隊(duì)列
我們都知道在iOS開發(fā)過程當(dāng)中或者是任何一門操作系統(tǒng)語言中,隊(duì)列都是一個(gè)很重要的數(shù)據(jù)結(jié)構(gòu),它有著FIFO(先進(jìn)先出)原則,根據(jù)操作系統(tǒng)內(nèi)核的不同,隊(duì)列又劃分為串行隊(duì)列 和 并發(fā)隊(duì)列。
接下來就讓我們從不同的地方取分析這兩種隊(duì)列。
3.1 串行隊(duì)列
串行隊(duì)列的概念
所有的調(diào)度任務(wù)進(jìn)入到任務(wù)棧以后,就由CPU統(tǒng)一調(diào)度,最先進(jìn)去的任務(wù)先調(diào)度,在調(diào)度任務(wù)未完成之前,其他任務(wù)不能被調(diào)度。這就是串行隊(duì)列,也就是相當(dāng)于排隊(duì)買票,一個(gè)一個(gè)來的進(jìn)行。

串行隊(duì)列的結(jié)構(gòu)
我們?cè)跍y(cè)試Demo中創(chuàng)建iOS開發(fā)過程中的常用幾種隊(duì)列,mainQueue,serialQueue,globalQueue,以及concurrentQueue, 并且答應(yīng)相關(guān)類的結(jié)構(gòu)可知
dispatch_queue_t serial = dispatch_queue_create("cooci", DISPATCH_QUEUE_SERIAL);
// OS_dispatch_queue_concurrent
dispatch_queue_t conque = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
// DISPATCH_QUEUE_SERIAL max && 1
// queue 對(duì)象 alloc init class
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// 多個(gè) - 集合
dispatch_queue_t globQueue = dispatch_get_global_queue(0, 0);
NSLog(@"%@-%@-%@-%@",serial,conque,mainQueue,globQueue);
打印結(jié)果如下
<OS_dispatch_queue_serial: cooci>-<OS_dispatch_queue_concurrent: cooci>-<OS_dispatch_queue_main: com.apple.main-thread>-<OS_dispatch_queue_global: com.apple.root.default-qos>
所以從上我們知道了主隊(duì)列也是串行隊(duì)列,只是不同于一般的串行隊(duì)列而已,從iOS GCD文檔中已經(jīng)標(biāo)記的很明白了。那么串行隊(duì)列的底層源碼是如何實(shí)現(xiàn)的呢?我們繼續(xù)探索
我們從以上的打印結(jié)果中知道,主隊(duì)列的打印結(jié)果是OS_dispatch_queue_main: com.apple.main-thread,我們進(jìn)入源碼搜索主線程的結(jié)果 能看到相應(yīng)的隊(duì)列定義
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,
};
3.2 并發(fā)隊(duì)列
并發(fā)隊(duì)列的概念:
所有的調(diào)度任務(wù)進(jìn)入到任務(wù)棧以后,就由CPU統(tǒng)一調(diào)度,最先進(jìn)去的任務(wù)先調(diào)度,但是由于計(jì)算機(jī)的CPU 在短暫的時(shí)間內(nèi)可以對(duì)多個(gè)調(diào)度任務(wù)進(jìn)行處理,利用時(shí)間片輪轉(zhuǎn)發(fā)來進(jìn)行任務(wù)的調(diào)度,所以就好像多個(gè)調(diào)度任務(wù)同時(shí)執(zhí)行的意思;

并發(fā)隊(duì)列的結(jié)構(gòu)
我們從以上的打印結(jié)果中知道,主隊(duì)列的打印結(jié)果是OS_dispatch_queue_concurrent,我們進(jìn)入源碼搜索主線程的結(jié)果 能看到相應(yīng)的隊(duì)列定義
struct dispatch_queue_global_s _dispatch_root_queues[] = {
_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,
),
3.3 隊(duì)列如何創(chuàng)建的并且關(guān)聯(lián)類信息
我們?cè)诖蛴〉慕Y(jié)果中已經(jīng)知道串行隊(duì)列打印的結(jié)構(gòu)是OS_dispatch_queue_serial ,并發(fā)隊(duì)列打印的結(jié)構(gòu)是OS_dispatch_queue_concurrent,接下來就讓我們進(jìn)入源碼看看隊(duì)列是如何關(guān)聯(lián)類對(duì)象并且isa指針的,
首先我們進(jìn)入到dispatch_queue_create函數(shù)看看源碼
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);
}
再次進(jìn)入到_dispatch_lane_create_with_target
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
dispatch_queue_t tq, bool legacy)
{
// dqai 創(chuàng)建 -
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
//
// Step 1: Normalize arguments (qos, overcommit, tq)
//
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_lane_s)); // alloc
_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0)); // init
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__);
return _dispatch_trace_queue_create(dq)._dq;
就這樣能進(jìn)行相關(guān)的隊(duì)列的創(chuàng)建過程;
以上創(chuàng)建過程中如果需要進(jìn)行相應(yīng)的串行和并發(fā)的判斷
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 {
vtable = DISPATCH_VTABLE(queue_serial);
}
然后我們進(jìn)入到DISPATCH_VTABLE中去看看是如何決定一個(gè)隊(duì)列是串行還是并發(fā)的
我們能找到相應(yīng)的宏定義是
define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)
再次搜索DISPATCH_OBJC_CLASS
我們也知道當(dāng)前是一個(gè)宏定義如下
#define OS_OBJECT_VTABLE(name) (&OS_OBJECT_CLASS_SYMBOL(name))
#define DISPATCH_OBJC_CLASS(name) (&DISPATCH_CLASS_SYMBOL(name))
我們?cè)俅稳炙阉?code>DISPATCH_CLASS_SYMBOL就能找到定義如下
#define OS_OBJECT_EXTRA_VTABLE_SYMBOL(name) _OS_##name##_vtable
#define DISPATCH_CLASS_SYMBOL(name) OS_dispatch_##name##_class
從以上的源碼定義我們就知道系統(tǒng)是通過拼接名字進(jìn)行的
通過以上創(chuàng)建的類進(jìn)行名字拼接就得到我們自己所定義的類。這就是隊(duì)列創(chuàng)建和關(guān)聯(lián)的過程。
四、總結(jié)
以上就是本人對(duì)隊(duì)列的創(chuàng)建和函數(shù)底層源碼的調(diào)用過程的學(xué)習(xí),由于libdispatch源碼晦澀難懂,所以我只能跟著源碼一步步查詢和學(xué)習(xí),有很多不足之處,請(qǐng)大聲多多指教。