前一文章介紹了GCD
的概念寞蚌、使用林螃,以及函數(shù)與隊(duì)列搭配情況纽竣,本文將就GCD
底層進(jìn)行分析坠韩。
隊(duì)列創(chuàng)建
準(zhǔn)備
libdispatch
工程卑笨。(進(jìn)入Apple的工程下載找到libdispatch
)
分析隊(duì)列創(chuàng)建就不得不在底層源碼中分析dispatch_queue_create
孕暇,我們查詢?cè)创a工程找到了以下定義:
dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
{
/**
label: 是隊(duì)列標(biāo)簽,比如自定義的 com.myQueue,系統(tǒng)的 com.apple.main-thread
*/
return _dispatch_lane_create_with_target(label, attr,
DISPATCH_TARGET_QUEUE_DEFAULT, true);
}
DISPATCH_NOINLINE
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
dispatch_queue_t tq, bool legacy)
{
// 創(chuàng)建 dqai 可以獲得是否為并發(fā) 或 串行隊(duì)列
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
//
// Step 1: Normalize arguments (qos, overcommit, tq)
// 規(guī)范化參數(shù): qos赤兴,overcommit妖滔,tq
dispatch_qos_t qos = dqai.dqai_qos;
#if !HAVE_PTHREAD_WORKQUEUE_QOS
if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
}
if (qos == DISPATCH_QOS_MAINTENANCE) {
dqai.dqai_qos = qos = DISPATCH_QOS_BACKGROUND;
}
#endif // !HAVE_PTHREAD_WORKQUEUE_QOS
_dispatch_queue_attr_overcommit_t overcommit = dqai.dqai_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");
}
}
//.........
//
// Step 2: Initialize the queue 初始化queue
//
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);
}
switch (dqai.dqai_autorelease_frequency) {
case DISPATCH_AUTORELEASE_FREQUENCY_NEVER:
dqf |= DQF_AUTORELEASE_NEVER;
break;
case DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM:
dqf |= DQF_AUTORELEASE_ALWAYS;
break;
}
if (label) {
const char *tmp = _dispatch_strdup_if_mutable(label);
if (tmp != label) {
dqf |= DQF_LABEL_NEEDS_FREE;
label = tmp;
}
}
// 創(chuàng)建隊(duì)列
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_lane_s));
// 初始化隊(duì)列;根據(jù)dqai.dqai_concurrent的值桶良,決定隊(duì)列 是 串行 還是并發(fā)
_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));
//.........
// 設(shè)置隊(duì)列標(biāo)識(shí)符
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__);
// dp作為研究對(duì)象
return _dispatch_trace_queue_create(dq)._dq;
}
_dispatch_trace_queue_create -> _dispatch_introspection_queue_create(dqu);
_dispatch_introspection_queue_create
對(duì)上一層創(chuàng)建之后的dq經(jīng)過兩層包裝座舍、處理再返回了dq
。
_dispatch_introspection_queue_create(dispatch_queue_t dq)
{
dispatch_queue_introspection_context_t dqic;
size_t sz = sizeof(struct dispatch_queue_introspection_context_s);
if (!_dispatch_introspection.debug_queue_inversions) {
sz = offsetof(struct dispatch_queue_introspection_context_s,
__dqic_no_queue_inversion);
}
dqic = _dispatch_calloc(1, sz);
dqic->dqic_queue._dq = dq;
if (_dispatch_introspection.debug_queue_inversions) {
LIST_INIT(&dqic->dqic_order_top_head);
LIST_INIT(&dqic->dqic_order_bottom_head);
}
dq->do_finalizer = dqic;
_dispatch_unfair_lock_lock(&_dispatch_introspection.queues_lock);
LIST_INSERT_HEAD(&_dispatch_introspection.queues, dqic, dqic_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 upcast(dq)._dqu;
}
需要研究_dispatch_lane_create_with_target
中做了哪些操作陨帆,都是圍繞dq
展開曲秉。
1、_dispatch_queue_attr_to_info創(chuàng)建類型對(duì)象疲牵,用來存儲(chǔ)隊(duì)列的相關(guān)信息承二。
2、設(shè)置隊(duì)列的關(guān)聯(lián)屬性
qos
等3纲爸、初始化隊(duì)列亥鸠;通過
DISPATCH_VTABLE
拼接隊(duì)列名稱
#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
#define DISPATCH_CLASS(name) OS_dispatch_##name
判斷dqai.dqai_concurrent
決定是串行隊(duì)列還是并發(fā)隊(duì)列。
并發(fā)隊(duì)列:
OS_dispatch_queue_serial
并發(fā)隊(duì)列:OS_dispatch_queue_concurrent
- 4识啦、
_dispatch_object_alloc
創(chuàng)建隊(duì)列
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; // 更改isa指向
return obj;
}
創(chuàng)建隊(duì)列過程中负蚊,有更改isa指向操作,說明隊(duì)列是對(duì)象颓哮。
- 5家妆、
_dispatch_queue_init
初始化dq
,設(shè)置一些屬性
// Note to later developers: ensure that any initialization changes are
// made for statically allocated queues (i.e. _dispatch_main_q).
static inline dispatch_queue_class_t
_dispatch_queue_init(dispatch_queue_class_t dqu, dispatch_queue_flags_t dqf,
uint16_t width, uint64_t initial_state_bits)
{
uint64_t dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(width);
dispatch_queue_t dq = dqu._dq;
dispatch_assert((initial_state_bits & ~(DISPATCH_QUEUE_ROLE_MASK |
DISPATCH_QUEUE_INACTIVE)) == 0);
if (initial_state_bits & DISPATCH_QUEUE_INACTIVE) {
dq->do_ref_cnt += 2; // rdar://8181908 see _dispatch_lane_resume
if (dx_metatype(dq) == _DISPATCH_SOURCE_TYPE) {
dq->do_ref_cnt++; // released when DSF_DELETED is set
}
}
dq_state |= initial_state_bits;
dq->do_next = DISPATCH_OBJECT_LISTLESS;
dqf |= DQF_WIDTH(width);
os_atomic_store2o(dq, dq_atomic_flags, dqf, relaxed);
dq->dq_state = dq_state;
dq->dq_serialnum =
os_atomic_inc_orig(&_dispatch_queue_serial_numbers, relaxed);
return dqu;
}
初始化完成之后設(shè)置dq_label、dq_priority
- 6题翻、
_dispatch_trace_queue_create -> _dispatch_introspection_queue_create -> return upcast(dq)._dqu;
,通過對(duì)隊(duì)列進(jìn)行操作并將處理完的dq
返回揩徊。
全局隊(duì)列
dispatch_queue_global_t
dispatch_get_global_queue(long priority, unsigned long flags)
{
dispatch_assert(countof(_dispatch_root_queues) ==
DISPATCH_ROOT_QUEUE_COUNT);
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;
}
return _dispatch_get_root_queue(qos, flags & DISPATCH_QUEUE_OVERCOMMIT);
}
// ----------------------
DISPATCH_ALWAYS_INLINE DISPATCH_CONST
static inline dispatch_queue_global_t
_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");
}
return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}
從底層獲取獲取合適的隊(duì)列,這可以佐證它的并發(fā)性嵌赠。
通過dispatch_queue_global_s
這個(gè)結(jié)構(gòu)體獲取全局各種屬性的global_quue
.
主隊(duì)列
/*!
* @function dispatch_get_main_queue
*
* @abstract
* Returns the default queue that is bound to the main thread.
*
* @discussion
* In order to invoke blocks submitted to the main queue, the application must
* call dispatch_main(), NSApplicationMain(), or use a CFRunLoop on the main
* thread.
*
* The main queue is meant to be used in application context to interact with the main thread and the main runloop.
*
* Because the main queue doesn't behave entirely like a regular serial queue,
* it may have unwanted side-effects when used in processes that are not UI apps
* (daemons). For such processes, the main queue should be avoided.
*
* @see dispatch_queue_main_t
*
* @result
* Returns the main queue. This queue is created automatically on behalf of the main thread before main() is called.
*/
DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_CONST DISPATCH_NOTHROW
dispatch_queue_main_t
dispatch_get_main_queue(void)
{
return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);
}
可以得到幾點(diǎn)信息:
1.它是綁定到主線程的默認(rèn)隊(duì)列;
2.主隊(duì)列用于在應(yīng)用程序上下文中與主線程和主運(yùn)行循環(huán)進(jìn)行交互
3.主隊(duì)列不想常規(guī)的串行隊(duì)列熄赡,在非UI應(yīng)用程序使用姜挺,反而會(huì)發(fā)生不好的作用,應(yīng)該避免
4.它是自動(dòng)創(chuàng)建的隊(duì)列彼硫,且發(fā)生在main()
調(diào)用之前炊豪。
通過查找dispatch_queue_main_t
凌箕,我們找到了它的定義,它是綁定到主線程的默認(rèn)隊(duì)列的類型词渤。也指出了主隊(duì)列是一個(gè)串行隊(duì)列牵舱。主隊(duì)列是一個(gè)眾所周知的全局對(duì)象,該對(duì)象自動(dòng)在在進(jìn)程初始化期間代表主線程缺虐,并由返回dispatch_get_main_queue()
芜壁,并且無法修改該對(duì)象。
由上面的信息:創(chuàng)建隊(duì)列發(fā)生在mian()
調(diào)用之前高氮,這可以聯(lián)想到dyld
的流程慧妄,在OC底層原理10—應(yīng)用程序加載中流程圖中簡(jiǎn)單介紹到main調(diào)用之前,初始化時(shí)會(huì)走底層的libdispatch_init
.
函數(shù):異步函數(shù) & 同步函數(shù)
異步函數(shù)
先通過打印異步函數(shù)的堆棧來看看異步函數(shù)調(diào)用過程中底層的調(diào)用順序
_dispatch_root_queue_drain -> _dispatch_continuation_pop_inline -> _dispatch_continuation_invoke_inline -> _dispatch_client_callout -> _dispatch_call_block_and_release
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_continuation_invoke_inline(dispatch_object_t dou,
dispatch_invoke_flags_t flags, dispatch_queue_class_t dqu)
{
dispatch_continuation_t dc = dou._dc, dc1;
dispatch_invoke_with_autoreleasepool(flags, {
uintptr_t dc_flags = dc->dc_flags;
// Add the item back to the cache before calling the function. This
// allows the 'hot' continuation to be used for a quick callback.
//
// The ccache version is per-thread.
// Therefore, the object has not been reused yet.
// This generates better assembly.
_dispatch_continuation_voucher_adopt(dc, dc_flags);
if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {
_dispatch_trace_item_pop(dqu, dou);
}
if (dc_flags & DC_FLAG_CONSUME) {
dc1 = _dispatch_continuation_free_cacheonly(dc);
} else {
dc1 = NULL;
}
if (unlikely(dc_flags & DC_FLAG_GROUP_ASYNC)) {
_dispatch_continuation_with_group_invoke(dc);
} else {
// 里面為f(ctxt)返回 ,實(shí)際就是 _dispatch_call_block_and_release
_dispatch_client_callout(dc->dc_ctxt, dc->dc_func);
//
_dispatch_trace_item_complete(dc);
}
if (unlikely(dc1)) {
_dispatch_continuation_free_to_cache_limit(dc1);
}
});
_dispatch_perfmon_workitem_inc();
}
// ---------
#undef _dispatch_client_callout
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
@try {
return f(ctxt); // 返回一個(gè)方法調(diào)用,實(shí)際為 _dispatch_call_block_and_release
}
@catch (...) {
objc_terminate();
}
}
有了堆棧調(diào)用順序的初步了解剪芍,再探究dispatch_async
源碼塞淹,如下:
void
dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
{
// 創(chuàng)建一個(gè)持續(xù)調(diào)度的對(duì)象
dispatch_continuation_t dc = _dispatch_continuation_alloc();
uintptr_t dc_flags = DC_FLAG_CONSUME;
dispatch_qos_t qos;
/**
初始化調(diào)度對(duì)象:包裝任務(wù)->接收work,保存work
*/
qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
// 并發(fā)處理
_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}
// --------------
DISPATCH_ALWAYS_INLINE
static inline dispatch_continuation_t
_dispatch_continuation_alloc(void)
{
dispatch_continuation_t dc =
_dispatch_continuation_alloc_cacheonly();
if (unlikely(!dc)) {
return _dispatch_continuation_alloc_from_heap();
}
return dc;
}
- _dispatch_continuation_init :任務(wù)包裝器
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)
{
// 拷貝任務(wù)
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; // 將拷貝的任務(wù)地址賦值
// 將初始化所有字段罪裹,但需要設(shè)置dc_flags和dc_ctxt
return _dispatch_continuation_init_slow(dc, dqu, flags);
}
//封裝work -> 同步回調(diào)函數(shù)
dispatch_function_t func = _dispatch_Block_invoke(work);
if (dc_flags & DC_FLAG_CONSUME) {
//異步回調(diào)函數(shù)
func = _dispatch_call_block_and_release;
}
// 給回調(diào)函數(shù)賦值
return _dispatch_continuation_init_f(dc, dqu, ctxt, func, flags, dc_flags);
}
// 0----------0
#define _dispatch_Block_invoke(bb) \
((dispatch_function_t)((struct Block_layout *)bb)->invoke)
//0---------0
void
_dispatch_call_block_and_release(void *block)
{
void (^b)(void) = block;
b();
Block_release(b);
}
小結(jié):_dispatch_continuation_init
包裝任務(wù)饱普,并設(shè)置線程的回調(diào)函數(shù),初始化調(diào)度對(duì)象状共,保存在qos
的屬性中套耕。
- _dispatch_continuation_async
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);
}
并發(fā)執(zhí)行block,其中dx_push(dqu._dq, dc, qos)
定義如下
#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
而dq_push
則根據(jù)傳入的隊(duì)列類型口芍,分別執(zhí)行以下等不同的函數(shù)
總結(jié):異步函數(shù)被調(diào)用時(shí)箍铲,會(huì)先創(chuàng)建一個(gè)持續(xù)調(diào)度隊(duì)列的對(duì)象
dc
,然后初始化調(diào)度對(duì)象鬓椭,并將待執(zhí)行任務(wù)包裝颠猴,pthread
開辟線程再通過invoke
執(zhí)行回調(diào)block,并發(fā)執(zhí)行不同的調(diào)度函數(shù)是由隊(duì)列的類型決定小染。
同步函數(shù)
dispatch_sync
源碼如下
DISPATCH_NOINLINE
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);
}
- _dispatch_sync_f 流程
DISPATCH_NOINLINE
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_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)) {
// 判斷 dq_width 是否為1翘瓮,表示一次只執(zhí)行一個(gè)。就返回柵欄函數(shù)的同步實(shí)現(xiàn)
return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags);
}
// 檢查隊(duì)列的類型是否正確 dispatch_sync
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
// 如果為全局并發(fā)隊(duì)列或者綁定到非調(diào)度線程的隊(duì)列裤翩,就進(jìn)行緩慢同步執(zhí)行资盅,其實(shí)做一些判斷之后最終還是要挨個(gè)執(zhí)行
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)) {
// 遞歸同步執(zhí)行,一個(gè)一個(gè)執(zhí)行踊赠,直到全部執(zhí)行完成
return _dispatch_sync_recurse(dl, ctxt, func, dc_flags);
}
// 內(nèi)部開始同步執(zhí)行
_dispatch_introspection_sync_begin(dl);
// 執(zhí)行完呵扛,執(zhí)行block回調(diào)
_dispatch_sync_invoke_and_complete(dl, ctxt, func DISPATCH_TRACE_ARG(
_dispatch_trace_item_sync_push_pop(dq, ctxt, func, dc_flags)));
}
??
DISPATCH_NOINLINE
static void
_dispatch_sync_recurse(dispatch_lane_t dq, void *ctxt,
dispatch_function_t func, uintptr_t dc_flags)
{
dispatch_tid tid = _dispatch_tid_self();
dispatch_queue_t tq = dq->do_targetq;
do {
if (likely(tq->dq_width == 1)) {
if (unlikely(!_dispatch_queue_try_acquire_barrier_sync(tq, tid))) {
return _dispatch_sync_f_slow(dq, ctxt, func, dc_flags, tq,
DC_FLAG_BARRIER);
}
} else {
dispatch_queue_concurrent_t dl = upcast(tq)._dl;
if (unlikely(!_dispatch_queue_try_reserve_sync_width(dl))) {
return _dispatch_sync_f_slow(dq, ctxt, func, dc_flags, tq, 0);
}
}
tq = tq->do_targetq;
} while (unlikely(tq->do_targetq));
_dispatch_introspection_sync_begin(dq);
_dispatch_sync_invoke_and_complete_recurse(dq, ctxt, func, dc_flags
DISPATCH_TRACE_ARG(_dispatch_trace_item_sync_push_pop(
dq, ctxt, func, dc_flags)));
}
??
DISPATCH_NOINLINE
static void
_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);
}
這是 _dispatch_sync_f -> _dispatch_sync_f_inline
流程,
- 在
_dispatch_sync_f_inline
中首先判斷dq_width
是否為1筐带,返回執(zhí)行柵欄同步函數(shù)實(shí)現(xiàn) - 判斷隊(duì)列類型是否為
dispatch_sync
- 判斷如果為全局并發(fā)隊(duì)列或者綁定到非調(diào)度線程的隊(duì)列今穿,就進(jìn)行緩慢同步執(zhí)行
- 判斷是否執(zhí)行遞歸調(diào)用
5.開始執(zhí)行同步執(zhí)行,執(zhí)行完成伦籍,回調(diào)block蓝晒。
結(jié)合堆棧分析
不論串行隊(duì)列下的同步還是并發(fā)隊(duì)列下的同步腮出,調(diào)用堆棧順序相同。相比異步函數(shù)堆棧芝薇,少了許多胚嘲,但在其底層還是做了許多的判斷與操作的。
- _dispatch_sync_block_with_privdata 流程
#ifdef __BLOCKS__
DISPATCH_NOINLINE
static void
_dispatch_sync_block_with_privdata(dispatch_queue_t dq, dispatch_block_t work,
uintptr_t dc_flags)
{
dispatch_block_private_data_t dbpd = _dispatch_block_get_data(work);
pthread_priority_t op = 0, p = 0;
dispatch_block_flags_t flags = dbpd->dbpd_flags;
if (flags & DISPATCH_BLOCK_BARRIER) {
dc_flags |= DC_FLAG_BLOCK_WITH_PRIVATE_DATA | DC_FLAG_BARRIER;
} else {
dc_flags |= DC_FLAG_BLOCK_WITH_PRIVATE_DATA;
}
op = _dispatch_block_invoke_should_set_priority(flags, dbpd->dbpd_priority);
if (op) {
p = dbpd->dbpd_priority;
}
voucher_t ov, v = DISPATCH_NO_VOUCHER;
if (flags & DISPATCH_BLOCK_HAS_VOUCHER) {
v = dbpd->dbpd_voucher;
}
ov = _dispatch_set_priority_and_voucher(p, v, 0);
// balanced in d_block_sync_invoke or d_block_wait
if (os_atomic_cmpxchg2o(dbpd, dbpd_queue, NULL, dq, relaxed)) {
_dispatch_retain_2(dq);
}
if (dc_flags & DC_FLAG_BARRIER) {
_dispatch_barrier_sync_f(dq, work, _dispatch_block_sync_invoke,
dc_flags);
} else {
_dispatch_sync_f(dq, work, _dispatch_block_sync_invoke, dc_flags);
}
_dispatch_reset_priority_and_voucher(op, ov);
}
這是柵欄同步函數(shù)流程洛二,但最終根據(jù)隊(duì)列標(biāo)記判斷不是柵欄函數(shù)還是要走_dispatch_sync_f
流程的馋劈。
補(bǔ)充「柵欄函數(shù)」
目的:控制任務(wù)同步執(zhí)行,即使是異步函數(shù)+并發(fā)隊(duì)列
方法:dispatch_barrier_async灭红、dispatch_barrier_sync
: 異步侣滩、同步柵欄函數(shù)都是能「阻攔」任務(wù)執(zhí)行。后者的區(qū)別是會(huì)阻塞線程变擒。
注意:柵欄函數(shù)只能控制同意并發(fā)隊(duì)列
使用:
dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
/* 1.異步函數(shù) */
dispatch_async(concurrentQueue, ^{
sleep(1);
NSLog(@"異步1:休息一下君珠,再做事!");
});
/* 2. 柵欄函數(shù) */
dispatch_barrier_async(concurrentQueue, ^{
NSLog(@"柵欄:== %@",[NSThread currentThread]);
});
/* 3. 異步函數(shù) */
dispatch_async(concurrentQueue, ^{
NSLog(@"異步2:終于可以到我干活了嗎");
});
NSLog(@"主線程:來活了");
//=========打印=========
[34550:1419250] 主線程:來活了
[34550:1419411] 異步1:休息一下娇斑,再做事策添!
[34550:1419412] 柵欄:== <NSThread: 0x600001bc5900>{number = 3, name = (null)}
[34550:1419412] 異步2:終于可以到我干活了嗎
單例
在平常開發(fā)中,經(jīng)常會(huì)使用到單例設(shè)計(jì)模式毫缆,保證一個(gè)類僅有一個(gè)實(shí)例唯竹,并提供一個(gè)訪問它的全局訪問點(diǎn)。主要解決的問題就是:避免一個(gè)全局使用的類頻繁地創(chuàng)建與銷毀苦丁。
一般編寫單例的形式如下:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//TODO:初始化
});
// --可以查看dispatch_once_t的類型浸颓,它是一個(gè)長(zhǎng)整型,通過取地址&做傳參---
typdef long dispatch_once_t;
在這里探究當(dāng)然是因?yàn)樗膶?shí)現(xiàn)就是發(fā)生在libdispatch.dylib
中的旺拉,所以溯源找到了以下定義
// once.c
void
dispatch_once(dispatch_once_t *val, dispatch_block_t block)
{
dispatch_once_f(val, block, _dispatch_Block_invoke(block));
}
//------
#define _dispatch_Block_invoke(bb) \
( (dispatch_function_t) ((struct Block_layout *)bb)->invoke )
typedef void (*dispatch_function_t)(void *_Nullable);
//------
DISPATCH_NOINLINE
void
dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
dispatch_once_gate_t l = (dispatch_once_gate_t)val;
// 非編譯優(yōu)化 或 采用一次靜默計(jì)數(shù)器方法产上。判斷是否已經(jīng)存在一個(gè)實(shí)例化單例,存在即返回
#if !DISPATCH_ONCE_INLINE_FASTPATH || DISPATCH_ONCE_USE_QUIESCENT_COUNTER
// 即創(chuàng)建一個(gè)標(biāo)記蛾狗,判斷是否等于 DLOCK_ONCE_DONE(表示已經(jīng)創(chuàng)建)
uintptr_t v = os_atomic_load(&l->dgo_once, acquire);
if (likely(v == DLOCK_ONCE_DONE)) {
return;
}
#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
// 嘗試加鎖
if (likely(DISPATCH_ONCE_IS_GEN(v))) {
// 加鎖失敗之后
return _dispatch_once_mark_done_if_quiesced(l, v);
}
#endif
#endif
//☆:嘗試進(jìn)入晋涣;判斷 l(即oncetokden)是否為DLOCK_ONCE_UNLOCKED
if (_dispatch_once_gate_tryenter(l)) {
return _dispatch_once_callout(l, ctxt, func);
}
return _dispatch_once_wait(l);
}
// ----dispatch_gate_s + dispatch_once_gate_s -----
typedef struct dispatch_gate_s {
dispatch_lock dgl_lock;
} dispatch_gate_s, *dispatch_gate_t;
typedef struct dispatch_once_gate_s {
union {
dispatch_gate_s dgo_gate;
uintptr_t dgo_once; // dgo_once 作為判斷已存在單例的標(biāo)志
};
} dispatch_once_gate_s, *dispatch_once_gate_t;
-
dispatch_once_t
是長(zhǎng)整型,定義成了一個(gè)靜態(tài)變量沉桌,首先就它而已是具有唯一性谢鹊。強(qiáng)轉(zhuǎn)類型之后得到l
,&l->dgo_once
就作為判斷是否已經(jīng)創(chuàng)建實(shí)例化的標(biāo)志。 -
#define DISPATCH_ONCE_IS_GEN(gen) (((gen) & 3) == DLOCK_FAILED_TRYLOCK_BIT)
留凭,在采用靜默計(jì)數(shù)器方法情況下佃扼,嘗試加鎖,加鎖失敗之后會(huì)執(zhí)行_dispatch_once_mark_done_if_quiesced
蔼夜。
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_once_mark_done_if_quiesced(dispatch_once_gate_t dgo, uintptr_t gen)
{
if (_dispatch_once_generation() - gen >= DISPATCH_ONCE_GEN_SAFE_DELTA) {
/*
* See explanation above, when the quiescing counter approach is taken
* then this store needs only to be relaxed as it is used as a witness
* that the required barriers have happened.
*/
// 再次存儲(chǔ)標(biāo)志松嘶,存儲(chǔ)值為:DLOCK_ONCE_DONE.
os_atomic_store(&dgo->dgo_once, DLOCK_ONCE_DONE, relaxed);
}
}
- 正常單例初始化創(chuàng)建,第一次就會(huì)走這個(gè)流程
_dispatch_once_gate_tryenter
挎扰,嘗試進(jìn)入執(zhí)行創(chuàng)建
DISPATCH_ALWAYS_INLINE
static inline bool
_dispatch_once_gate_tryenter(dispatch_once_gate_t l)
{
// 先對(duì)比&l->dgo_once是否等于無鎖DLOCK_ONCE_UNLOCKED翠订;
// 1、如果相等則更改其值遵倦,為_dispatch_lock_value_for_self() (即給自己加鎖)尽超,返回YES
// 2、如果不等梧躺,則DLOCK_ONCE_UNLOCKED賦值給l->dgo_once似谁,最后NO
return os_atomic_cmpxchg(&l->dgo_once, DLOCK_ONCE_UNLOCKED,
(uintptr_t)_dispatch_lock_value_for_self(), relaxed);
}
#define os_atomic_cmpxchg(p, e, v, m) \
({ _os_atomic_basetypeof(p) _r = (e); \
atomic_compare_exchange_strong_explicit(_os_atomic_c11_atomic(p), \
&_r, v, memory_order_##m, memory_order_relaxed); })
嘗試對(duì)比l->dgo_once 與 DLOCK_ONCE_UNLOCKED
是否相同.根據(jù)對(duì)比結(jié)果返回YES\NO
. 如果為YES
,則_dispatch_once_callout
執(zhí)行block回調(diào)掠哥,同時(shí)向外廣播狀態(tài)巩踏,拒絕再次訪問創(chuàng)建,续搀。為NO塞琼,則執(zhí)行_dispatch_once_wait
,即在當(dāng)前實(shí)例創(chuàng)建過程中,會(huì)加鎖禁舷,當(dāng)有需求再次創(chuàng)建時(shí)彪杉,會(huì)一直等待,除非鎖被釋放牵咙。
DISPATCH_NOINLINE
static void
_dispatch_once_callout(dispatch_once_gate_t l, void *ctxt,
dispatch_function_t func)
{
// 執(zhí)行方法塊 func派近,即block
_dispatch_client_callout(ctxt, func);
// 對(duì)外發(fā)送廣播,當(dāng)前已經(jīng)有執(zhí)行單例創(chuàng)建洁桌,拒絕再訪問
_dispatch_once_gate_broadcast(l);
}
// --_dispatch_client_callout--
#undef _dispatch_client_callout
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
@try {
return f(ctxt);
}
@catch (...) {
objc_terminate();
}
}
// --_dispatch_once_gate_broadcast--
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_once_gate_broadcast(dispatch_once_gate_t l)
{
dispatch_lock value_self = _dispatch_lock_value_for_self();
uintptr_t v;
#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
v = _dispatch_once_mark_quiescing(l);//標(biāo)記為停頓
#else
v = _dispatch_once_mark_done(l); // 標(biāo)記 DLOCK_ONCE_DONE
#endif
// 如果 v已經(jīng)是自鎖狀態(tài)渴丸,即已經(jīng)是初始化創(chuàng)建過單例對(duì)象 l(onceToken) = DLOCK_ONCE_DONE,就直接返回
if (likely((dispatch_lock)v == value_self)) return;
_dispatch_gate_broadcast_slow(&l->dgo_gate, (dispatch_lock)v);
}
// ----_dispatch_once_mark_done--
DISPATCH_ALWAYS_INLINE
static inline uintptr_t
_dispatch_once_mark_done(dispatch_once_gate_t dgo)
{
//DLOCK_ONCE_DONE 賦值給 dgo->dgo_once 另凌,然后上鎖谱轨,標(biāo)記DLOCK_ONCE_DONE(-1),即onceToken 的值為 DLOCK_ONCE_DONE途茫。
return os_atomic_xchg(&dgo->dgo_once, DLOCK_ONCE_DONE, release);
}
//
#define os_atomic_xchg(p, v, m) \
atomic_exchange_explicit(_os_atomic_c11_atomic(p), v, memory_order_##m)
總結(jié)
【單例流程】先判斷l
是否第一次碟嘴,如果為第一次來,則將DLOCK_ONCE_UNLOCKED
賦值給 l->dgo_once
囊卜,先加鎖_dispatch_lock_value_for_self()
娜扇;返回YES執(zhí)行_dispatch_once_callout
調(diào)用block
創(chuàng)建;
第二次時(shí)栅组,即實(shí)例已經(jīng)創(chuàng)建雀瓢,判斷os_atomic_load ()即 v
是否等于一個(gè) DLOCK_ONCE_DONE
,如果等于玉掸,直接return刃麸。
【線程安全】有多個(gè)線程進(jìn)來時(shí),dispatch_lock_value_for_self()
加鎖了司浪,保證了同一時(shí)間僅有一個(gè)線程在處理泊业。當(dāng)前線程未處理外把沼,后來者將保持等待狀態(tài)_dispatch_once_wait()
,直到鎖解除吁伺。