iOS 中的 GCD 實現詳解

清水之靈發表於2019-02-27

實現Dispatch Queue需要的軟體元件(程式碼)

iOS 中的 GCD 實現詳解

開發者所使用的GCD的API實現庫地址: libdispatch

以下僅是對Dispatch Queue的分析。

GCD佇列是按照層級關係來組織的:

iOS 中的 GCD 實現詳解

GCD中的幾種主要的資料結構:

GCD中的佇列Dispatch Queue是通過連結串列和結構體實現的。

佇列的繼承關係:

iOS 中的 GCD 實現詳解

GCD中常用的結構體和巨集

// 宣告一個結構體,可以看出gcd內部name_t和name_s之間的關係:name_s是一個指向結構體name_t的指標。一個東西
#define DISPATCH_DECL(name) typedef struct name##_s *name##_t
複製程式碼
// dispatch_object_t可以是聯合內部任何一種結構(僅保留Queue相關的)
typedef union {
	struct _os_object_s *_os_obj;// 基類
	struct dispatch_object_s *_do;// 基類繼承os_object
	struct dispatch_continuation_s *_dc;// 任務結構
	struct dispatch_queue_s *_dq;// 佇列結構
	struct dispatch_queue_attr_s *_dqa;// 佇列相關屬性
	struct dispatch_group_s *_dg;// group結構
	struct dispatch_semaphore_s *_dsema;// 訊號量
} dispatch_object_t DISPATCH_TRANSPARENT_UNION;
複製程式碼
// 系統物件結構或者說系統基類
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;

// 系統物件頭部定義巨集
#define _OS_OBJECT_HEADER(isa, ref_cnt, xref_cnt) 
        isa;    // isa指標
        int volatile ref_cnt; // gcd物件內部引用計數
        int volatile xref_cnt // gcd物件外部引用計數(內外部都要減到0時,物件會被釋放)
        
複製程式碼
struct dispatch_object_s {
	_DISPATCH_OBJECT_HEADER(object);
};
#define _DISPATCH_OBJECT_HEADER(x) 
	struct _os_object_s _as_os_obj[0]; //繼承自os_object
	OS_OBJECT_STRUCT_HEADER(dispatch_##x); 
	struct dispatch_##x##_s *volatile do_next; //連結串列的next
	struct dispatch_queue_s *do_targetq; 
	void *do_ctxt; 
	void *do_finalizer
複製程式碼
// GCD中的任務是先被封裝成dispatch_continuation,再提交到佇列中的。該dispatch_continuation用於儲存任務所在的dispatch_group和一些其他資訊,相當於一般常說的執行上下文。
typedef struct dispatch_continuation_s {
	struct dispatch_object_s _as_do[0];//繼承自dispatch_object
	DISPATCH_CONTINUATION_HEADER(continuation);//continuation的一些屬性
} *dispatch_continuation_t;
  
#define DISPATCH_CONTINUATION_HEADER(x) 
	union { 
		const void *do_vtable; 
		uintptr_t dc_flags; 
	}; 
	union { 
		pthread_priority_t dc_priority; 
		int dc_cache_cnt; 
		uintptr_t dc_pad; 
	}; 
	struct voucher_s *dc_voucher; 
	struct dispatch_##x##_s *volatile do_next; 
	dispatch_function_t dc_func; // 任務函式(block會轉為function)
	void *dc_ctxt; // 執行環境:函式引數
	void *dc_data; 
	void *dc_other
複製程式碼
// 佇列結構體
struct dispatch_queue_s {
	_DISPATCH_QUEUE_HEADER(queue);
	DISPATCH_QUEUE_CACHELINE_PADDING; // for static queues only
} DISPATCH_ATOMIC64_ALIGN;

#define _DISPATCH_QUEUE_HEADER(x) 
	struct os_mpsc_queue_s _as_oq[0]; 
	DISPATCH_OBJECT_HEADER(x); //繼承父類的屬性
	uint32_t dq_side_suspend_cnt; 
	DISPATCH_UNION_LE(uint32_t volatile dq_atomic_flags, 
		const uint16_t dq_width, //寬度:序列1,並行>1
		const uint16_t __dq_opaque 
	); 
	DISPATCH_INTROSPECTION_QUEUE_HEADER
	
#define DISPATCH_INTROSPECTION_QUEUE_HEADER 
		TAILQ_ENTRY(dispatch_queue_s) diq_list; 
		dispatch_unfair_lock_s diq_order_top_head_lock; 
		dispatch_unfair_lock_s diq_order_bottom_head_lock; 
		TAILQ_HEAD(, dispatch_queue_order_entry_s) diq_order_top_head; // 佇列連結串列頭結點
		TAILQ_HEAD(, dispatch_queue_order_entry_s) diq_order_bottom_head // 尾節點
  
// 佇列屬性
struct dispatch_queue_attr_s {
	OS_OBJECT_STRUCT_HEADER(dispatch_queue_attr);
};

複製程式碼
typedef struct dispatch_continuation_vtable_s {
	_OS_OBJECT_CLASS_HEADER();
	DISPATCH_INVOKABLE_VTABLE_HEADER(dispatch_continuation);
} const *dispatch_continuation_vtable_t;

#define DISPATCH_INVOKABLE_VTABLE_HEADER(x) 
	unsigned long const do_type; // 型別
	const char *const do_kind; // 種類描述
	void (*const do_invoke)(struct x##_s *, dispatch_invoke_context_t, 
			dispatch_invoke_flags_t); // 執行函式
	void (*const do_push)(struct x##_s *, dispatch_object_t, 
			dispatch_qos_t)// 推入佇列函式

序列佇列例項:
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_serial, queue,
	.do_type = DISPATCH_QUEUE_SERIAL_TYPE,
	.do_kind = "serial-queue",
	.do_dispose = _dispatch_queue_dispose,
	.do_suspend = _dispatch_queue_suspend,
	.do_resume = _dispatch_queue_resume,
	.do_finalize_activation = _dispatch_queue_finalize_activation,
	.do_push = _dispatch_queue_push,
	.do_invoke = _dispatch_queue_invoke,
	.do_wakeup = _dispatch_queue_wakeup,
	.do_debug = dispatch_queue_debug,
	.do_set_targetq = _dispatch_queue_set_target_queue,
);


複製程式碼

GCD中的幾種主要API:

FIFO佇列的管理是通過dispatch_async等函式來實現的(操作頭尾節點)。
在GCD中,可執行的任務有兩種方式實現:Block、Function。因此和任務相關的API一般是有兩種形式的:

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
dispatch_async_f(dispatch_queue_t queue,
	void *_Nullable context,
	dispatch_function_t work);
複製程式碼

獲取全域性佇列

  1. 實現程式碼
dispatch_queue_t
dispatch_get_global_queue(long priority, unsigned long flags)
{
        // 造成空值返回的原因  
	if (flags & ~(unsigned long)DISPATCH_QUEUE_OVERCOMMIT) {
		return DISPATCH_BAD_INPUT;
	} 
        // 轉化優先順序
	dispatch_qos_t qos = _dispatch_qos_from_queue_priority(priority);
#if !HAVE_PTHREAD_WORKQUEUE_QOS
	if (qos == QOS_CLASS_MAINTENANCE) {
		qos = DISPATCH_QOS_BACKGROUND;
	} else if (qos == QOS_CLASS_USER_INTERACTIVE) {
		qos = DISPATCH_QOS_USER_INITIATED;
	}
#endif
	if (qos == DISPATCH_QOS_UNSPECIFIED) {
		return DISPATCH_BAD_INPUT;
	}
	// flags傳0時: overcommit = NO
	// 從系統佇列陣列中獲取對應優先順序的陣列
	return _dispatch_get_root_queue(qos, flags & DISPATCH_QUEUE_OVERCOMMIT);
}
複製程式碼
static inline dispatch_queue_t
_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
{
	// _dispatch_root_queues是系統中儲存不同級別佇列的陣列,返回陣列中指定index的佇列,overcommit=0/1
	return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}

// 陣列中的佇列
struct dispatch_queue_s _dispatch_root_queues[];
// skip zero
// 1 - main_q  主佇列
// 2 - mgr_q  gcd內部管理佇列
// 3 - mgr_root_q
// 4,5,6,7,8,9,10,11,12,13,14,15 - global queues
// 之後是手動建立的佇列

複製程式碼
dispatch_qos_t:
#define DISPATCH_QOS_UNSPECIFIED            ((dispatch_qos_t)0)
#define DISPATCH_QOS_MAINTENANCE            ((dispatch_qos_t)1)
#define DISPATCH_QOS_BACKGROUND             ((dispatch_qos_t)2)
#define DISPATCH_QOS_UTILITY                ((dispatch_qos_t)3)
#define DISPATCH_QOS_DEFAULT                ((dispatch_qos_t)4)
#define DISPATCH_QOS_USER_INITIATED         ((dispatch_qos_t)5)
#define DISPATCH_QOS_USER_INTERACTIVE       ((dispatch_qos_t)6)

系統中佇列的優先順序列舉:
enum {
	DISPATCH_ROOT_QUEUE_IDX_MAINTENANCE_QOS = 0,
	DISPATCH_ROOT_QUEUE_IDX_MAINTENANCE_QOS_OVERCOMMIT,
	DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_QOS,
	DISPATCH_ROOT_QUEUE_IDX_BACKGROUND_QOS_OVERCOMMIT,
	DISPATCH_ROOT_QUEUE_IDX_UTILITY_QOS,
	DISPATCH_ROOT_QUEUE_IDX_UTILITY_QOS_OVERCOMMIT,
	DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS,
	DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_OVERCOMMIT,
	DISPATCH_ROOT_QUEUE_IDX_USER_INITIATED_QOS,
	DISPATCH_ROOT_QUEUE_IDX_USER_INITIATED_QOS_OVERCOMMIT,
	DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS,
	DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS_OVERCOMMIT,
	_DISPATCH_ROOT_QUEUE_IDX_COUNT,
};
複製程式碼
  1. 全域性佇列的優先順序有8中
    iOS 中的 GCD 實現詳解

    除了正常的4種外,還有overcommit的四種

/*!
 * @enum 佇列的一個標誌位
 *
 * @constant DISPATCH_QUEUE_OVERCOMMIT
 * 表示當執行緒池中執行緒用完(eg:64個執行緒),不管系統多麼繁忙,不會等待,這個佇列都會強制建立一個新的執行緒來執行任務。也就是說可以建立超過核心數目的執行緒。
 */
enum {
	DISPATCH_QUEUE_OVERCOMMIT = 0x2ull,
};
複製程式碼
  1. 程式碼測試:overcommit = YES
    iOS 中的 GCD 實現詳解

    overcommit = NO

    iOS 中的 GCD 實現詳解

主佇列

  1. 實現程式碼:
struct dispatch_queue_s _dispatch_main_q = {
	DISPATCH_GLOBAL_OBJECT_HEADER(queue_main),
#if !DISPATCH_USE_RESOLVERS
        // 主佇列的目標佇列是:預設優先順序,overcommit的globalQueue
	.do_targetq = &_dispatch_root_queues[
			DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_OVERCOMMIT],
#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_CANNOT_TRYSYNC | DQF_WIDTH(1),
	.dq_serialnum = 1,
};
複製程式碼

建立佇列

  1. 程式碼實現
dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
{
	return _dispatch_queue_create_with_target(label, attr,
			DISPATCH_TARGET_QUEUE_DEFAULT(NULL), true);
}

static dispatch_queue_t
_dispatch_queue_create_with_target(const char *label, dispatch_queue_attr_t dqa,
		dispatch_queue_t tq, bool legacy)
{
	//
	// Step 1: Normalize arguments (qos, overcommit, tq)
	//

	dispatch_qos_t qos = _dispatch_priority_qos(dqa->dqa_qos_and_relpri);

	_dispatch_queue_attr_overcommit_t overcommit = dqa->dqa_overcommit;
	if (overcommit != _dispatch_queue_attr_overcommit_unspecified && tq) {
		if (tq->do_targetq) {
			DISPATCH_CLIENT_CRASH(tq, "Cannot specify both overcommit and "
					"a non-global target queue");
		}
	}

	if (tq && !tq->do_targetq &&
			tq->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) {
		// Handle discrepancies between attr and target queue, attributes win
		if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
			if (tq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) {
				overcommit = _dispatch_queue_attr_overcommit_enabled;
			} else {
				overcommit = _dispatch_queue_attr_overcommit_disabled;
			}
		}
		if (qos == DISPATCH_QOS_UNSPECIFIED) {
			dispatch_qos_t tq_qos = _dispatch_priority_qos(tq->dq_priority);
			tq = _dispatch_get_root_queue(tq_qos,
					overcommit == _dispatch_queue_attr_overcommit_enabled);
		} else {
			tq = NULL;
		}
	} else if (tq && !tq->do_targetq) {
		// target is a pthread or runloop root queue, setting QoS or overcommit
		// is disallowed
		if (overcommit != _dispatch_queue_attr_overcommit_unspecified) {
			DISPATCH_CLIENT_CRASH(tq, "Cannot specify an overcommit attribute "
					"and use this kind of target queue");
		}
	} else {
		if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
			 // 
			overcommit = dqa->dqa_concurrent ?
					_dispatch_queue_attr_overcommit_disabled :
					_dispatch_queue_attr_overcommit_enabled;
		}
	}
	if (!tq) {
	// 手動建立佇列,未設定目標佇列,則從系統佇列中獲取一個佇列(預設優先順序的globalQueue)作為目標佇列,如果建立的是序列佇列,則目標佇列是overcommit,否則不是(?)
		tq = _dispatch_get_root_queue(
				qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos,
				overcommit == _dispatch_queue_attr_overcommit_enabled);
	}

	//
	// Step 2: Initialize the queue
	//

	if (legacy) {
		// if any of these attributes is specified, use non legacy classes
		if (dqa->dqa_inactive || dqa->dqa_autorelease_frequency) {
			legacy = false;
		}
	}

	const void *vtable;
	dispatch_queue_flags_t dqf = 0;
	if (legacy) {
		vtable = DISPATCH_VTABLE(queue);
	} else if (dqa->dqa_concurrent) {
		vtable = DISPATCH_VTABLE(queue_concurrent);
	} else {
		vtable = DISPATCH_VTABLE(queue_serial);
	}
	switch (dqa->dqa_autorelease_frequency) {
	case DISPATCH_AUTORELEASE_FREQUENCY_NEVER:
		dqf |= DQF_AUTORELEASE_NEVER;
		break;
	case DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM:
		dqf |= DQF_AUTORELEASE_ALWAYS;
		break;
	}
	if (legacy) {
		dqf |= DQF_LEGACY;
	}
	if (label) {
		const char *tmp = _dispatch_strdup_if_mutable(label);
		if (tmp != label) {
			dqf |= DQF_LABEL_NEEDS_FREE;
			label = tmp;
		}
	}
        // 建立佇列物件
        // 分配空間 isa指向它的型別 相關屬性賦值
	dispatch_queue_t dq = _dispatch_object_alloc(vtable,
			sizeof(struct dispatch_queue_s) - DISPATCH_QUEUE_CACHELINE_PAD);
		// 初始化佇列相關引數
	_dispatch_queue_init(dq, dqf, dqa->dqa_concurrent ?
			DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
			(dqa->dqa_inactive ? DISPATCH_QUEUE_INACTIVE : 0));

	dq->dq_label = label;
	dq->dq_priority = dqa->dqa_qos_and_relpri;
	if (!dq->dq_priority) {
		// 未設定優先順序的佇列,優先順序預設繼承自它的目標佇列
		_dispatch_queue_priority_inherit_from_target(dq, tq);
	} else if (overcommit == _dispatch_queue_attr_overcommit_enabled) {
		dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
	}
	if (!dqa->dqa_inactive) {
		_dispatch_queue_inherit_wlh_from_target(dq, tq);
	}
	_dispatch_retain(tq);
	// 設定目標佇列
	dq->do_targetq = tq;
	_dispatch_object_debug(dq, "%s", __func__);
	return _dispatch_introspection_queue_create(dq);
}
// 構建佇列內部頭尾節點等
dispatch_queue_t
_dispatch_introspection_queue_create(dispatch_queue_t dq)
{
	TAILQ_INIT(&dq->diq_order_top_head);// 建立佇列的頭尾節點
	TAILQ_INIT(&dq->diq_order_bottom_head);
	_dispatch_unfair_lock_lock(&_dispatch_introspection.queues_lock);// 加鎖
	TAILQ_INSERT_TAIL(&_dispatch_introspection.queues, dq, diq_list);// 將佇列插入到佇列陣列中
	_dispatch_unfair_lock_unlock(&_dispatch_introspection.queues_lock);// 解鎖

	DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_create, dq);
	if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_create)) {
		_dispatch_introspection_queue_create_hook(dq);
	}
	return dq;
}

複製程式碼

設定目標佇列

  1. 實現程式碼:
void
_dispatch_queue_set_target_queue(dispatch_queue_t dq, dispatch_queue_t tq)
{
        // global/main queue
	dispatch_assert(dq->do_ref_cnt != DISPATCH_OBJECT_GLOBAL_REFCNT &&
			dq->do_targetq);
        // tq為空時,設定預設的目標佇列同createQ
	if (unlikely(!tq)) {
		bool is_concurrent_q = (dq->dq_width > 1);
		tq = _dispatch_get_root_queue(DISPATCH_QOS_DEFAULT, !is_concurrent_q);
	}

	if (_dispatch_queue_try_inactive_suspend(dq)) {
		_dispatch_object_set_target_queue_inline(dq, tq);
		return dx_vtable(dq)->do_resume(dq, false);
	}

	if (unlikely(!_dispatch_queue_is_legacy(dq))) {
#if 1
		if (_dispatch_queue_atomic_flags(dq) & DQF_TARGETED) {
			DISPATCH_CLIENT_CRASH(0, "Cannot change the target of a queue "
					"already targeted by other dispatch objects");
		}
#endif
		DISPATCH_CLIENT_CRASH(0, "Cannot change the target of this object "
				"after it has been activated");
	}

	unsigned long type = dx_type(dq);
	switch (type) {
	case DISPATCH_QUEUE_LEGACY_TYPE:
#if 1
		if (_dispatch_queue_atomic_flags(dq) & DQF_TARGETED) {
			_dispatch_bug_deprecated("Changing the target of a queue "
					"already targeted by other dispatch objects");
		}
#endif
		break;
	case DISPATCH_SOURCE_KEVENT_TYPE:
	case DISPATCH_MACH_CHANNEL_TYPE:
		_dispatch_ktrace1(DISPATCH_PERF_post_activate_retarget, dq);
		_dispatch_bug_deprecated("Changing the target of a source "
				"after it has been activated");
		break;
	default:
		DISPATCH_CLIENT_CRASH(type, "Unexpected dispatch object type");
	}

	_dispatch_retain(tq);
	return _dispatch_barrier_trysync_or_async_f(dq, tq,
			_dispatch_queue_legacy_set_target_queue,
			DISPATCH_BARRIER_TRYSYNC_SUSPEND);
}
複製程式碼

向佇列中同步新增任務

  1. 實現程式碼:
// 任務是block、function走的都是一樣的
void
dispatch_sync(dispatch_queue_t dq, dispatch_block_t work)
{
	dispatch_sync_f(dq, work, _dispatch_Block_invoke(work));
	// _dispatch_Block_invoke:block轉function
}

複製程式碼
void
dispatch_sync_f(dispatch_queue_t dq, void *ctxt//函式引數, dispatch_function_t func)
{
// 同步任務一般不切換執行緒
        // 如果是序列佇列,則等待前面的任務執行完成才能開始執行,barrier通過訊號量來實現的
	if (likely(dq->dq_width == 1)) {
		return dispatch_barrier_sync_f(dq, ctxt, func);
	}

	// 當前佇列阻塞barrier或者執行緒數超過了佇列的容量,可能需要切換執行緒
	if (unlikely(!_dispatch_queue_try_reserve_sync_width(dq))) {
		return _dispatch_sync_f_slow(dq, ctxt, func, 0);
	}
        // 如果想在當前序列佇列中同步加入任務 死鎖
	_dispatch_introspection_sync_begin(dq);
	if (unlikely(dq->do_targetq->do_targetq)) {
		return _dispatch_sync_recurse(dq, ctxt, func, 0);
	}
	_dispatch_sync_invoke_and_complete(dq, ctxt, func);// 同步執行函式
}
複製程式碼

注意死鎖:

iOS 中的 GCD 實現詳解

向佇列中非同步新增任務

  1. 實現程式碼:
dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
{
        // 將任務包裝成continuation
	dispatch_continuation_t dc = _dispatch_continuation_alloc();
	uintptr_t dc_flags = DISPATCH_OBJ_CONSUME_BIT;

	_dispatch_continuation_init(dc, dq, work, 0, 0, dc_flags);
	_dispatch_async_f2(dq, dc);// 中間省略了幾步
}

static void
_dispatch_async_f2(dispatch_queue_t dq, dispatch_continuation_t dc)
{
	if (slowpath(dq->dq_items_tail)) {// 如果當前佇列中尾節點存在,說明當前佇列中有任務了,就不需要再把任務在向上提交了,直接提交到當前佇列中。
		return 	dx_push(dq, dc, _dispatch_continuation_override_qos(dq, dc));
	}

	if (slowpath(!_dispatch_queue_try_acquire_async(dq))) {
		return 	dx_push(dq, dc, _dispatch_continuation_override_qos(dq, dc));
	}
        // 否則需要沿著鏈往上提交任務
	return _dispatch_async_f_redirect(dq, dc,
			_dispatch_continuation_override_qos(dq, dc));
}
// 將任務轉發
_dispatch_async_f_redirect(dispatch_queue_t dq,
		dispatch_object_t dou, dispatch_qos_t qos)
{
	dq = dq->do_targetq;

	// 根據佇列的繼承鏈,將任務一層層向上提交,直到目標佇列是序列佇列,其實就是將任務提交到了目標佇列中了。
	// #define DISPATCH_QUEUE_USES_REDIRECTION(width) 
		({ uint16_t _width = (width); 
		_width > 1 && _width < DISPATCH_QUEUE_WIDTH_POOL; })
		
	while (slowpath(DISPATCH_QUEUE_USES_REDIRECTION(dq->dq_width))) { // 省略中間內容
		dq = dq->do_targetq;
	}

	dx_push(dq, dou, qos);
}

dx_push:

static inline void
_dispatch_queue_push_inline(dispatch_queue_t dq, dispatch_object_t _tail,
		dispatch_qos_t qos)
{
        // 更新佇列尾節點指標,插入新任務節點
	struct dispatch_object_s *tail = _tail._do;
	dispatch_wakeup_flags_t flags = 0;
	bool overriding = _dispatch_queue_need_override_retain(dq, qos);
	if (unlikely(_dispatch_queue_push_update_tail(dq, tail))) {
		if (!overriding) _dispatch_retain_2(dq->_as_os_obj);
		_dispatch_queue_push_update_head(dq, tail);
		flags = DISPATCH_WAKEUP_CONSUME_2 | DISPATCH_WAKEUP_MAKE_DIRTY;
	} else if (overriding) {
		flags = DISPATCH_WAKEUP_CONSUME_2;
	} else {
		return;
	}
	return dx_wakeup(dq, qos, flags);// 喚起下個任務
}

複製程式碼

獲取當前佇列

dispatch_get_current_queue()

系統中佇列是按照層級關係來組織的,獲取當前佇列,也就是要獲取執行當前程式碼的執行緒所關聯的佇列,由於目標佇列的這種繼承關係,任務會一層層向上提交至根佇列,所以dispatch_get_current_queue()獲取的返回結果可能是當前執行環境中關聯的佇列中的任意一個與預期不同,因此使用它可能會造成錯誤,甚至是死鎖問題。

如以下程式碼:由於dispatch_get_current_queue()結果的錯誤會執行else語句,如果當前佇列就是queue,就會造成同步等待(訊號量鎖實現,因此也是死鎖問題)

void func(dispatch_queue_t queue, dispatch_block_t block)
{
    if (dispatch_get_current_queue() == queue) {
        block();
    }else{
        dispatch_sync(queue, block);
    }
}
複製程式碼
如何實現獲取當前佇列:

dispatch_queue_set_specific(queue,key) // 給佇列設定一個關聯key

dispatch_get_specific(key) // 獲取到當前任務的執行環境(一串繼承鏈的佇列)中對應key值的佇列。這個就更加準確了。

    dispatch_queue_t q1 = dispatch_queue_create("", NULL);
    dispatch_queue_t q3 = dispatch_queue_create("", NULL);
    dispatch_set_target_queue(q3, q1);

    static int specificKey;
    CFStringRef specificValue = CFSTR("queue1");
    dispatch_queue_set_specific(q1,
                                &specificKey,
                                (void*)specificValue,
                                (dispatch_function_t)CFRelease);
    
    
    dispatch_sync(q3, ^{
        dispatch_block_t block = ^{
            //do something
        };
        // 看看關聯當前這個正在執行的任務的佇列鏈條中是否有key值是specificKey的佇列即q1
        CFStringRef retrievedValue = dispatch_get_specific(&specificKey);
        if (retrievedValue) { // 有,直接執行block
            block();
        } else { // 沒有,同步加到q1中,不會造成同步阻塞
            dispatch_sync(q1, block);
        }
    });
複製程式碼

實現程式碼:

void *
dispatch_get_specific(const void *key)
{
	if (slowpath(!key)) {
		return NULL;
	}
	void *ctxt = NULL;
	dispatch_queue_t dq = _dispatch_queue_get_current();// 獲取當前任務是在哪個佇列鏈中執行的

	while (slowpath(dq)) {// 沿著鏈找關聯key值的佇列
		ctxt = _dispatch_queue_get_specific_inline(dq, key);
		if (ctxt) break;
		dq = dq->do_targetq;
	}
	return ctxt;
}
複製程式碼

可重入概念

GCD使用

一般當下載資料時,為了不妨礙主執行緒的執行,會進行非同步處理。那麼每次網路下載處理都要使用GCD的執行緒嗎?答案是否定的。如果每次下載就生成一個執行緒,那麼很可能會產生大量的執行緒,很快就會用盡執行緒池中執行緒和記憶體。因此係統提供的非同步網路通訊API,它生成一個執行緒專門用於非同步下載,同時開啟這個執行緒的runloop,新增port讓runloop一直迴圈執行,這樣執行緒就不會被銷燬,當有下載任務時,該執行緒被喚醒執行任務,沒任務就掛起等待。

相關文章