筆記-GCD源碼簡單解析之隊列與函數

前面兩篇文章,簡單的探究了一下GCD,如今經過源碼解析一下GCD的底層實現,看本文內容時,最好對照源碼閱讀,效果會好不少數組

隊列

GCD隊列的建立,不外乎兩種狀況,串行和併發bash

dispatch_queue_create("com.zb.cn", NULL);      // 串行
dispatch_queue_create("com.zb.cn", DISPATCH_QUEUE_CONCURRENT);     // 併發
複製代碼

上面完整的輸出了串行和併發隊列的信息,下面就經過底層代碼看看是如何進行建立的。

下面代碼是依次在源碼裏的走向,省略了不重要代碼:併發

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(const char *label, dispatch_queue_attr_t dqa,
		dispatch_queue_t tq, bool legacy)
{
	
	// 一、結構體dqai
	dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
	.....
}
複製代碼

首先經過dispatch_queue_create()調用_dispatch_lane_create_with_target()方法,同時注意兩個參數。 而後觀察方法裏的第一行代碼dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);,觀察過源碼的小夥伴應該能夠了解,後面的代碼都是圍繞這個結構體來寫的。app

_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
		dispatch_queue_t tq, bool legacy)
{
	
	// 一、結構體dqai
	dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
	
	if (!tq) {
		tq = _dispatch_get_root_queue(
				qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, // 4
				overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq; // 0 1
		if (unlikely(!tq)) {
			DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
		}
	}
        // 此處省略一些代碼
	if (dqai.dqai_concurrent) {
		// 經過dqai.dqai_concurrent 來區分併發和串行
		// OS_dispatch_queue_concurrent_class
		vtable = DISPATCH_VTABLE(queue_concurrent);
	} else {
		vtable = DISPATCH_VTABLE(queue_serial);
	}

	// 開闢內存 - 生成響應的對象 queue
	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);
	_dispatch_retain(tq);
	dq->do_targetq = tq;
	_dispatch_object_debug(dq, "%s", __func__);
	return _dispatch_trace_queue_create(dq)._dq;
}
複製代碼

經過上面的閱讀,咱們先明確一個問題,串行和併發隊列是如何建立的?或者說,底層經過什麼來區別隊列的?
咱們在源碼裏能夠看到一個很顯眼的關鍵字queue_concurrentqueue_serial,那麼就是經過對dqai.dqai_concurrent的判斷,來區分串行和併發。異步

if (dqai.dqai_concurrent) {
	// 經過dqai.dqai_concurrent 來區分併發和串行
	// OS_dispatch_queue_concurrent_class
	vtable = DISPATCH_VTABLE(queue_concurrent);
} else {
	vtable = DISPATCH_VTABLE(queue_serial);
}
複製代碼

那麼咱們就去尋找一下dqai.dqai_concurrent是在何時賦值的,這個方法裏搜索,實際上是找不到對這個屬性的賦值的,因此咱們就去查找一下這個dqai的建立async

dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
複製代碼

隊列的建立是經過dispatch_queue_create方法,以及兩個參數,回到源碼,看結構體daqi的建立(此處省略了無關代碼)。函數

_dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa)
{
	dispatch_queue_attr_info_t dqai = { };
	if (!dqa) return dqai;

	dqai.dqai_concurrent = !(idx % DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT);
	idx /= DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT;
}
複製代碼

在這裏咱們看到了對dqai_concurrent的賦值。那麼就分析一下這個方法,首先建立了一個空的結構體dqai,下面就直接判斷參數dqa,若是是空,就直接返回,不然就往下走。經過代碼走向,能夠知道,其實這個參數,就是dispatch_queue_create()的第二個參數,串行傳NULL,併發傳的是非NULL學習

意味着若是是串行,dqai.dqai_concurrent就是爲空,再回過頭看一下,底層就是經過dqai.dqai_concurrent來區分併發和串行的。ui

驚奇發現一:

vtable = DISPATCH_VTABLE(queue_concurrent) & DISPATCH_VTABLE(queue_serial);
解析一下DISPATCH_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
這裏就獲得了一個重要的信息 OS_dispatch_##name##_class,這裏的##name##就是傳入的參數,替換參數: OS_dispatch_queue_serial_classOS_dispatch_queue_concurrent_class
你們在回過頭去看一下本文一開始就打印的兩個隊列的詳細信息,彷佛明白了點什麼!!!atom

好,這裏咱們找到了底層是如何區分併發和串行的,下面就看看,是如何建立隊列的,下面截取源碼裏關鍵的代碼分析

// 開闢內存 - 生成相應的對象 queue
	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));

複製代碼

也給你們寫了註釋,經過_dispatch_object_alloc()開闢內存,生成相應的對象,而後經過_dispatch_queue_init()進行初始化。

驚奇發現二:

解析一下構造方法_dispatch_queue_init()
dqai.dqai_concurrent ? DISPATCH_QUEUE_WIDTH_MAX : 1
這裏的DISPATCH_QUEUE_WIDTH_MAX也是一個宏定義
#define DISPATCH_QUEUE_WIDTH_MAX (DISPATCH_QUEUE_WIDTH_FULL - 2)
#define DISPATCH_QUEUE_WIDTH_FULL 0x1000ull
小夥伴們能夠拿計算器算一下,而後會發現0x1000 - 2 = 0xffe
這段代碼的意思是:若是是併發隊列就是0xffe,串行隊列就是1,即0x1
在回過頭看一下本文一開始就打印的兩個隊列的詳細信息的截圖,是否是又彷佛明白了點什麼!!!

那麼這個到底width表明什麼呢? 串行和併發隊列的區別是啥呢?是否有種腦洞大開的感受呢?

對比一下這四個隊列的區別,是否你們和我有一樣的疑問呢!
同爲併發隊列,爲何global和自定義生成的width會不一樣?globaltarget爲什麼又與其餘三個不同呢?以及Mainglobal的名字又爲何是com.apple.main-threadcom.apple.root.default-qosMain又爲何是串行的隊列呢? 帶着這些疑問,咱們去尋找答案。

經過觀察源碼,找到target的賦值

dq->do_targetq = tq;
複製代碼

接着去尋找tq的賦值,源碼以下:

_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
		dispatch_queue_t tq, bool legacy)
{
	if (!tq) {
		tq = _dispatch_get_root_queue(
			qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, // 4
			overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq; // 0 1
		if (unlikely(!tq)) {
			DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
		}
	}
}
	
_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
{
	if (unlikely(qos < DISPATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) {
		DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");
	}
	// 4-1= 3
	// 2*3+0/1 = 6/7
	return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}
複製代碼

最終咱們會走到一個初始化好的一個裝了不少target_queue的靜態數組dispatch_root_queues[]

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,
	),
};
複製代碼

驚奇發現三:

發現數組裏有這段代碼
DISPATCH_GLOBAL_OBJECT_HEADER(queue_global)
DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL) 能夠理解爲queue_globalwidth==DISPATCH_QUEUE_WIDTH_POOL,查看這個宏
#define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1)
#define DISPATCH_QUEUE_WIDTH_FULL 0x1000ull
是否是感受特別的熟悉,在驚奇發現二里,咱們就找到裏自定義併發隊列的width=0xffe,這裏也恰好驗證裏全局隊列的width=0xfff,那麼到底爲何要這樣作呢?這個就要問蘋果爸爸了。。。(蘋果爸爸留了後路吧😅)
同時發現數組dispatch_root_queues[]裏的第6個元素就是com.apple.root.default-qos,到這裏咱們找到裏target的賦值

回想上面所說的,在建立串行或者併發隊列時,咱們是經過開闢內存生成了相應的對象,在後面給對象的target賦值的

// 開闢內存 - 生成相應的對象 queue
	dispatch_lane_t dq = _dispatch_object_alloc(vtable,
			sizeof(struct dispatch_lane_s));
	
        dq->do_targetq = tq;
複製代碼

再看一下全局隊列是如何建立的?

dispatch_get_global_queue(long priority, unsigned long flags)
{
        // 此處省略一系列操做
        dispatch_qos_t qos = _dispatch_qos_from_queue_priority(priority);
	return _dispatch_get_root_queue(qos, flags & DISPATCH_QUEUE_OVERCOMMIT);
}
複製代碼

能夠得出結論,global_queue是經過_dispatch_get_root_queue直接獲取獲得的,並無對target賦值的過程。

再看一下爲何global的名字是com.apple.root.default-qos,經過上面代碼,咱們須要進入_dispatch_qos_from_queue_priority()方法。

_dispatch_qos_from_queue_priority(long priority)
{
	switch (priority) {
	case DISPATCH_QUEUE_PRIORITY_BACKGROUND:      return DISPATCH_QOS_BACKGROUND;
	case DISPATCH_QUEUE_PRIORITY_NON_INTERACTIVE: return DISPATCH_QOS_UTILITY;
	case DISPATCH_QUEUE_PRIORITY_LOW:             return DISPATCH_QOS_UTILITY;
	case DISPATCH_QUEUE_PRIORITY_DEFAULT:         return DISPATCH_QOS_DEFAULT;
	case DISPATCH_QUEUE_PRIORITY_HIGH:            return DISPATCH_QOS_USER_INITIATED;
	default: return _dispatch_qos_from_qos_class((qos_class_t)priority);
	}
}
複製代碼

建立global_queue時,兩個參數傳的都是0,因此走到這個方法裏,就須要case 0:,這裏有個坑就是DISPATCH_QUEUE_PRIORITY_DEFAULT爲0,並非第一行的DISPATCH_QUEUE_PRIORITY_BACKGROUND,不相信的小夥伴能夠全局搜索一下這幾個宏,會發現

#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

而後能夠獲得返回的值爲4

#define DISPATCH_QOS_DEFAULT ((dispatch_qos_t)4)

再一次回到這個方法

_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
{
	if (unlikely(qos < DISPATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) {
		DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");
	}
	// 4-1= 3
	// 2*3+0/1 = 6/7
	return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}
複製代碼

驚奇發現四:

傳入的第一個參數qos的值爲4,第二個參數overcommit = 0 & DISPATCH_QUEUE_OVERCOMMIT
DISPATCH_QUEUE_OVERCOMMIT = 0x2ull,能夠得出overcommit=0
經過上面的方法裏的計算方式:2 * (qos - 1) + overcommit得出數組下標爲6
再一次進入到全局靜態數組裏能夠得出,global的名字是com.apple.root.default-qos的緣由。

接着看後面的問題是關於主隊列的,看下主隊列的建立方式

dispatch_get_main_queue(void)
{
	return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);
}
複製代碼

分別看一下這兩個參數dispatch_queue_main_t_dispatch_main_q的意義 進入dispatch_queue_main_t

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

經過上面代碼的分析DISPATCH_DECL_SUBCLASS(dispatch_queue_main, dispatch_queue_serial);,能夠獲得dispatch_queue_main是對dispatch_queue_serial重寫。

進入_dispatch_main_q

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,
};
複製代碼

驚奇發現五:

經過對上面靜態結構體的觀察,經過dq_label = "com.apple.main-thread"能夠知道爲何queue_main的名字是com.apple.main-thread
queue_main又爲何是串行隊列呢?由於他是對dispatch_queue_serial的重寫

到這裏咱們解決了全部問題,知道了底層是如何建立隊列,如何去區分串行和併發的,串行和併發隊列的width又爲何是不同的,以及它們和主隊列、全局隊列的一系列區別。隊列咱們暫時就分析到這裏。

函數

GCD的函數只有兩個,同步dispatch_sync()和異步dispatch_async()

同步dispatch_sync()

同步函數,不會開啓線程,按順序執行,須要注意的就是可能會產生死鎖。那麼回到源碼,看源碼裏是怎麼處理這一系列騷操做的。 跟着源碼走,最終會走到下面函數裏

_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_sync_invoke_and_complete(dl, ctxt, func DISPATCH_TRACE_ARG(
			_dispatch_trace_item_sync_push_pop(dq, ctxt, func, dc_flags)));
}
複製代碼

從上面代碼能夠看到,若是是併發隊列的話,可直接調用_dispatch_sync_invoke_and_complete()方法。串行則會走方法_dispatch_barrier_sync_f()

_dispatch_barrier_sync_f_inline(dispatch_queue_t dq, void *ctxt,
		dispatch_function_t func, uintptr_t dc_flags)
{
	// 獲取線程ID -- mach pthread --
	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");
	}
	
	// 死鎖
	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);
	}
    
	_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_sync_f_slow(dispatch_queue_class_t top_dqu, void *ctxt,
		dispatch_function_t func, uintptr_t top_dc_flags,
		dispatch_queue_class_t dqu, uintptr_t dc_flags)
{
	// 線程 -- 隊列 - 進來 push
	_dispatch_trace_item_push(top_dq, &dsc);
	__DISPATCH_WAIT_FOR_QUEUE__(&dsc, dq);

	_dispatch_introspection_sync_begin(top_dq);
	_dispatch_trace_item_pop(top_dq, &dsc);
	_dispatch_sync_invoke_and_complete_recurse(top_dq, ctxt, func,top_dc_flags
			DISPATCH_TRACE_ARG(&dsc));
}

__DISPATCH_WAIT_FOR_QUEUE__(dispatch_sync_context_t dsc, dispatch_queue_t dq)
{
	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");
	}
}

_dq_state_drain_locked_by(uint64_t dq_state, dispatch_tid tid)
{
	return _dispatch_lock_is_locked_by((dispatch_lock)dq_state, tid);
}

_dispatch_lock_is_locked_by(dispatch_lock lock_value, dispatch_tid tid)
{
	// ^ 兩個相同就會出現 0
	return ((lock_value ^ tid) & DLOCK_OWNER_MASK) == 0;
}
複製代碼

方法_dispatch_lock_is_locked_by()進行判斷,將要被調度的和等待的是同一個,那麼異或操做以後就是0==0,返回YES,產生死鎖。若是沒有產生死鎖,則執行_dispatch_trace_item_pop(),至關於把任務出棧執行調用。

任務的調度

_dispatch_sync_invoke_and_complete_recurse(dispatch_queue_class_t dq,
		void *ctxt, dispatch_function_t func, uintptr_t dc_flags
		DISPATCH_TRACE_ARG(void *dc))
{
	_dispatch_sync_function_invoke_inline(dq, ctxt, func);
	_dispatch_trace_item_complete(dc);
	_dispatch_sync_complete_recurse(dq._dq, NULL, dc_flags);
}
複製代碼

經過源碼傳參,上面源碼裏的func,就是函數的block,接着往方法_dispatch_sync_function_invoke_inline()

_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);       // f(ctxt) -- func(ctxt)
	_dispatch_perfmon_workitem_inc();
	_dispatch_thread_frame_pop(&dtf);
}
複製代碼
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
	return f(ctxt);
}
複製代碼

經過上面源碼能夠看到,函數方法的調用。

異步dispatch_async()

異步調用這裏主要看是如何建立線程

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);
}
複製代碼

_dispatch_continuation_init()方法主要是把任務塊進行初始化

_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

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

而後搜索dq_push(),就會看到對dq_push賦值,主要看queue_global

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_root_queue_push接着走,走到方法_dispatch_root_queue_poke_slow()

_dispatch_root_queue_poke_slow(dispatch_queue_global_t dq, int n, int floor)
{
	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;
	}
        // 此處省略一系列代碼
	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);
}

複製代碼

從上面代碼能夠看到底層是如何開闢線程的。對於全局隊列使用_pthread_workqueue_addthreads()的方法,其餘隊列則使用pthread_create()方法。

關於GCD底層函數簡單的解析就說這麼多。若是有什麼錯誤,望你們可以指出,一塊兒學習。

相關文章
相關標籤/搜索