GCD原理分析中篇

這是我參與8月更文挑戰的第4天,活動詳情查看:8月更文挑戰markdown

_dispatch_object_alloc

在creat的底層源碼中,申請和開闢內存使用的是這行代碼:併發

dispatch_lane_t dq = _dispatch_object_alloc(vtable,
			sizeof(struct dispatch_lane_s));
複製代碼

_dispatch_object_alloc作了什麼?從源碼一窺究竟異步

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

_dispatch_object_alloc -> _os_object_alloc_realizedasync

_os_object_t
_os_object_alloc_realized(const void *cls, size_t size)
{
	dispatch_assert(size >= sizeof(struct _os_object_s));
	return _os_objc_alloc(cls, size);
}
複製代碼

_dispatch_object_alloc -> _os_object_alloc_realized -> _os_objc_alloc 果真是在開闢內存空間函數

GCD底層源碼繼承鏈

在研究上面的問題以前,咱們繼續來查看全局隊列的源碼 image.pngpost

  • 主隊列的類型是:dispatch_queue_main_t
  • 全局隊列的類型是:dispatch_queue_global_s

由代碼咱們知道,無論是全局隊列仍是主隊列都是能夠使用dispatch_queue_t來接收的 DISPATCH_DECL(dispatch_queue);ui

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

也就等價於 OS_OBJECT_DECL_SUBCLASS(dispatch_queue, dispatch_object)繼續點擊,發現跳不進去了,因此咱們須要到源碼裏面去看了code

#define OS_OBJECT_DECL_SUBCLASS(name, super) \ OS_OBJECT_DECL_IMPL(name, NSObject, <OS_OBJECT_CLASS(super)>)
#define OS_OBJECT_DECL_SUBCLASS(name, super) DISPATCH_DECL(name)
複製代碼

全局搜索了以後,定位到了這兩處的宏定義,然而咱們發現第二處的宏定義正是咱們進來的地方,因此此次沒有研究的意義了。主要看第一個等價於orm

OS_OBJECT_DECL_IMPL(dispatch_queue, NSObject, <OS_OBJECT_CLASS(dispatch_object)>)

#define OS_OBJECT_DECL_IMPL(name, adhere, ...) \ OS_OBJECT_DECL_PROTOCOL(name, __VA_ARGS__) \ typedef adhere<OS_OBJECT_CLASS(name)> \ * OS_OBJC_INDEPENDENT_CLASS name##_t
複製代碼

等價於:

OS_OBJECT_DECL_PROTOCOL(dispatch_queue, dispatch_object) typedef NSObject<OS_dispatch_queue)> * OS_OBJC_INDEPENDENT_CLASS dispatch_queue_t

#define OS_OBJECT_DECL_PROTOCOL(name, ...) \ @protocol OS_OBJECT_CLASS(name) __VA_ARGS__ \ @end
複製代碼

等價於:

@protocol OS_OBJECT_CLASS(dispatch_queue) dispatch_object @end

#define OS_OBJECT_CLASS(name) OS_##name
複製代碼

等價於:

OS_dispatch_queue

或者還有一個更簡單的:

#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_t底層是一個結構體dispatch_queue_s繼承於dispatch_object_s能夠類比class,咱們知道class的底層繼承關係

class -> object_class -> object_object

dispatch_queue_t -> dispatch_queue_s -> dispatch_object_s -> _os_object_s ->dispatch_object_t

dispatch_queue_s

就跟研究類同樣,可想而知咱們重點研究的是dispatch_queue_s

struct dispatch_queue_s {
	DISPATCH_QUEUE_CLASS_HEADER(queue, void *__dq_opaque1);
	/* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN;

複製代碼

繼續搜索DISPATCH_QUEUE_CLASS_HEADER

#define _DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__) \ DISPATCH_OBJECT_HEADER(x); \ DISPATCH_UNION_LE(uint64_t volatile dq_state, \ dispatch_lock dq_state_lock, \ uint32_t dq_state_bits \ ); \ __pointer_sized_field__
複製代碼

等價於: DISPATCH_OBJECT_HEADER(queue);

#define DISPATCH_OBJECT_HEADER(x) \ struct dispatch_object_s _as_do[0]; \ _DISPATCH_OBJECT_HEADER(x)
複製代碼

最終指向了dispatch_object_s,也恰好驗證了上面的繼承過程。接着繼續搜索_DISPATCH_OBJECT_HEADER

#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; \ union { \ dispatch_function_t DISPATCH_FUNCTION_POINTER do_finalizer; \ void *do_introspection_ctxt; \ }
複製代碼

咱們發現原來在dispatch_object_s後面還有一層,還繼承了_os_object_s 繼續搜索宏定義OS_OBJECT_STRUCT_HEADER,拆了這麼多的包裝以後終於拿到了dispatch_queue_s的內部結構,發現一共有3個成員變量

#define OS_OBJECT_STRUCT_HEADER(x) \ _OS_OBJECT_HEADER(\ const void *_objc_isa, \ do_ref_cnt, \ do_xref_cnt); \ const struct x##_vtable_s *do_vtable
#else
複製代碼

GCD任務執行堆棧-同步

dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"同步函數分析");
    });
複製代碼

咱們來研究一下這個NSLog執行的時機,仍是看源碼

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

咱們只須要關注work的流向:_dispatch_sync_f關注第二個和第三個參數:ctxt, func

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

繼續搜索_dispatch_sync_f_inline

_dispatch_sync_f_inline(dispatch_queue_t dq, void *ctxt,
		dispatch_function_t func, uintptr_t dc_flags)
{
	if (likely(dq->dq_width == 1)) {
	 // 這裏有ctxt和func的調用
		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))) {
        // // 這裏有ctxt和func的調用
		return _dispatch_sync_f_slow(dl, ctxt, func, 0, dl, dc_flags);
	}

	if (unlikely(dq->do_targetq->do_targetq)) {
        // // 這裏有ctxt和func的調用
		return _dispatch_sync_recurse(dl, ctxt, func, dc_flags);
	}
	_dispatch_introspection_sync_begin(dl);
    // // 這裏有ctxt和func的調用
	_dispatch_sync_invoke_and_complete(dl, ctxt, func DISPATCH_TRACE_ARG(
			_dispatch_trace_item_sync_push_pop(dq, ctxt, func, dc_flags)));
}
複製代碼

經過在項目中下符號斷點能夠發現調用了_dispatch_barrier_sync_f -> _dispatch_sync_f_slow -> _dispatch_sync_function_invoke -> _dispatch_sync_function_invoke_inline -> _dispatch_client_callout

static inline void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
	return f(ctxt);
}
複製代碼

要驗證的話很簡單看項目中的堆棧或者在控制檯直接bt一下就能夠。 ​

GCD任務執行堆棧-異步

dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"異步函數分析");
    });
複製代碼

跟上面同步函數的思路同樣咱們到源碼裏面去分析:

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

異步函數的work通過一系列封裝以後賦值給了qos,定位到了這裏_dispatch_continuation_async,重點關注qos,結果這裏直接return了,線索到了這裏就斷了

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是一個宏

#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
複製代碼

上面的qos在第三個參數,因此咱們這裏重點要研究的是dq_push(x, y, z)

image.png

直接定位到全局併發隊列這裏

.dq_push        = _dispatch_root_queue_push,
複製代碼

_dispatch_continuation_async -> dq_push -> _dispatch_root_queue_push -> _dispatch_root_queue_push_inline -> _dispatch_root_queue_poke -> _dispatch_root_queue_poke_slow -> _dispatch_root_queues_init ->_dispatch_root_queues_init_once

static inline void
_dispatch_root_queues_init(void)
{
	dispatch_once_f(&_dispatch_root_queues_pred, NULL,
			_dispatch_root_queues_init_once);
}
複製代碼

定位到了單例dispatch_once_f _dispatch_root_queues_init_once -> _dispatch_worker_thread2 -> _dispatch_root_queue_drain -> _dispatch_continuation_pop_inline -> _dispatch_continuation_invoke_inline -> _dispatch_client_callout

_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
	return f(ctxt); //就是一個調用執行
}
複製代碼

這個異步函數過程相對比較複雜和漫長 image.png

相關文章
相關標籤/搜索