前言
- 復習 多線程GCD底層探索(上)
- 主要內容:探索單例、柵欄函數、信號量的拓展及底層實現流程。
準備
一 、單例 dispatch_once底層分析
創(chuàng)建單例:
static HwProtocolManager *manager = nil;
static dispatch_once_t onceToken;
+ (instancetype)manager
{
dispatch_once(& onceToken, ^{
NSLog(@"單例");
manager = [[HwProtocolManager alloc] init];
});
return manager;
}
對于單例我們知道,單例的流程只會執(zhí)行一次,為什么只執(zhí)行一次呢?我們來研究它的底層:
- 進入
dispatch_once源碼實現,底層是通過dispatch_once_f實現的- 參數1:
onceToken,它是一個靜態(tài)變量,static修飾,由于不同位置定義的靜態(tài)變量是不同的,所以靜態(tài)變量具有唯一性 - 參數2:
block回調
- 參數1:
void
dispatch_once(dispatch_once_t *val, dispatch_block_t block)
{
dispatch_once_f(val, block, _dispatch_Block_invoke(block));
}
進入dispatch_once_f源碼分析如下
DISPATCH_NOINLINE
void
dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
//1.將val,也就是靜態(tài)變量轉換為dispatch_once_gate_t類型的變量l
dispatch_once_gate_t l = (dispatch_once_gate_t)val;
#if !DISPATCH_ONCE_INLINE_FASTPATH || DISPATCH_ONCE_USE_QUIESCENT_COUNTER
//2.通過os_atomic_load獲取此時的任務的標識符v
uintptr_t v = os_atomic_load(&l->dgo_once, acquire);//load
//如果v等于DLOCK_ONCE_DONE,表示任務已經執(zhí)行過了,直接return
if (likely(v == DLOCK_ONCE_DONE)) {//已經執(zhí)行過了,直接返回
return;
}
#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
if (likely(DISPATCH_ONCE_IS_GEN(v))) {
//3.如果 任務執(zhí)行后,加鎖失敗了,則走到_dispatch_once_mark_done_if_quiesced函數,再次進行存儲,將標識符置為DLOCK_ONCE_DONE
return _dispatch_once_mark_done_if_quiesced(l, v);
}
#endif
#endif
//4.反之,則通過_dispatch_once_gate_tryenter嘗試進入任務,即解鎖,然后執(zhí)行_dispatch_once_callout執(zhí)行block回調
if (_dispatch_once_gate_tryenter(l)) {//嘗試進入
return _dispatch_once_callout(l, ctxt, func);
}
return _dispatch_once_wait(l);//無限次等待
}
_dispatch_once_gate_tryenter 解鎖
DISPATCH_ALWAYS_INLINE
static inline bool
_dispatch_once_gate_tryenter(dispatch_once_gate_t l)
{
return os_atomic_cmpxchg(&l->dgo_once, DLOCK_ONCE_UNLOCKED,
(uintptr_t)_dispatch_lock_value_for_self(), relaxed);//首先對比,然后進行改變
}
查看其源碼,主要是通過底層os_atomic_cmpxchg方法進行對比,如果比較沒有問題,則進行加鎖,即任務的標識符置為DLOCK_ONCE_UNLOCKED
_dispatch_once_callout 回調
DISPATCH_NOINLINE
static void
_dispatch_once_callout(dispatch_once_gate_t l, void *ctxt,
dispatch_function_t func)
{
_dispatch_client_callout(ctxt, func);//block調用執(zhí)行
_dispatch_once_gate_broadcast(l);//進行廣播:告訴別人有了歸屬,不要找我了
-
進入
_dispatch_once_callout源碼,主要就兩步_dispatch_client_callout:block回調執(zhí)行_dispatch_once_gate_broadcast:進行廣播
進入_dispatch_client_callout源碼,
#undef _dispatch_client_callout
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
@try {
return f(ctxt);
}
@catch (...) {
objc_terminate();
}
}
主要就是執(zhí)行block回調,其中的f等于_dispatch_Block_invoke(block),即異步回調
進入_dispatch_once_gate_broadcast -> _dispatch_once_mark_done源碼,
DISPATCH_ALWAYS_INLINE
static inline uintptr_t
_dispatch_once_mark_done(dispatch_once_gate_t dgo)
{
//如果不相同,直接改為相同,然后上鎖 -- DLOCK_ONCE_DONE
return os_atomic_xchg(&dgo->dgo_once, DLOCK_ONCE_DONE, release);
}
主要就是給dgo->dgo_once一個值,然后將任務的標識符為DLOCK_ONCE_DONE,即解鎖
針對單例的底層實現,總結如下:
【單例只執(zhí)行一次的原理】:GCD單例中,有兩個重要參數,
onceToken 和 block,其中onceToken是靜態(tài)變量,具有唯一性,在底層被封裝成了dispatch_once_gate_t類型的變量l,l主要是用來獲取底層原子封裝性的關聯,即變量v,通過v來查詢任務的狀態(tài),如果此時v等于DLOCK_ONCE_DONE,說明任務已經處理過一次了,直接return【block調用時機】:如果此時任務沒有執(zhí)行過,則會在底層通過
C++函數的比較,將任務進行加鎖,即任務狀態(tài)置為DLOCK_ONCE_UNLOCK,目的是為了保證當前任務執(zhí)行的唯一性,防止在其他地方有多次定義。加鎖之后進行block回調函數的執(zhí)行,執(zhí)行完成后,將當前任務解鎖,將當前的任務狀態(tài)置為DLOCK_ONCE_DONE,在下次進來時,就不會在執(zhí)行,會直接返回【多線程影響】:如果在當前任務執(zhí)行期間,有其他任務進來,會進入無限次等待,原因是當前任務已經獲取了鎖,進行了加鎖,其他任務是無法獲取鎖的
單例的底層流程分析如下如所示

二 、柵欄函數的拓展和底層源碼分析
iOS 底層探索:多線程GCD的使用 已經初步介紹過柵欄函數的使用,在這里再次進行拓展分析一波兒。
以前有個面試題類似如下
當時問:這樣寫對嗎?為什么?答案是不對的,會崩潰,為什么?經過我實際驗證有兩種方式不讓它崩潰,當然還有更多的方法,暫不說明。

這種方法是給可變數組一個固定的容量。
再看第二種方式如下:

這種方法是給異步線程一個柵欄函數控制線程。
首先我們分析為什么為什么會崩潰:
我們再崩潰的堆棧中可以看到如下:

崩潰之前調用了
-[__NSArrayM insertObject:atIndex:]這個函數,我們再objc底層源碼中去查看如下:
- (id)insertObject:anObject at:(unsigned)index
{
register id *this, *last, *prev;
if (! anObject) return nil;
if (index > numElements)
return nil;
if ((numElements + 1) > maxElements) {
volatile id *tempDataPtr;
/* we double the capacity, also a good size for malloc */
// 這里在數組超過一定的空間之后就進行了雙倍的擴容
maxElements += maxElements + 1;
// 這里數組tempDataPtr 進行了realloc操作 所以在多個線程同時訪問的時候就會出現問題
tempDataPtr = (id *) realloc (dataPtr, DATASIZE(maxElements));
dataPtr = (id*)tempDataPtr;
}
this = dataPtr + numElements;
prev = this - 1;
last = dataPtr + index;
while (this > last)
*this-- = *prev--;
*last = anObject;
numElements++;
return self;
}
- (id)addObject:anObject
{
return [self insertObject:anObject at:numElements];
}
這段就是可變數組添加數據時候底層實現,可以很清晰的看到,當數組的容量超過一定的maxElements的時候就會maxElements += maxElements + 1;,并且進行realloc重新創(chuàng)建了一個新的數組的操作,在多線程的操作,如果數組添加的元素太多就會出現給舊數組添加元素的時候,舊的數組其實已經被替代的情況,這樣就出現了崩潰。

可以看到并不會崩潰!!!
所以經過分析,我們得出結論,異步并發(fā)執(zhí)行addObject的時候會造成數組指針賦值錯誤的崩潰情況,當數組的容量maxElements固定之后就不會重新realloc ,就避免了同時訪問數組失敗的問題,但是我們的第二種不讓其崩潰的解決方法使用了柵欄函dispatch_barrier_async使線程安全。
那么柵欄函數dispatch_barrier_async是怎么做到的呢??
異步柵欄函數 底層分析如下:
進入dispatch_barrier_async源碼實現
#ifdef __BLOCKS__
void
dispatch_barrier_async(dispatch_queue_t dq, dispatch_block_t work)
{
dispatch_continuation_t dc = _dispatch_continuation_alloc();
uintptr_t dc_flags = DC_FLAG_CONSUME | DC_FLAG_BARRIER;
dispatch_qos_t qos;
qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
_dispatch_continuation_async(dq, dc, qos, dc_flags);
}
#endif
看到就會發(fā)現,我們上一篇分析的dispatch_async的底層實現,dispatch_async的本質其實就是dispatch_barrier_async,所以這里就不在往下分析了,可以查看上一篇的內容。
GCD中常用的柵欄函數,主要有兩種
同步柵欄函數dispatch_barrier_sync(在主線程中執(zhí)行):前面的任務執(zhí)行完畢才會來到這里,但是同步柵欄函數會堵塞線程,影響后面的任務執(zhí)行
異步柵欄函數dispatch_barrier_async:前面的任務執(zhí)行完畢才會來到這里
柵欄函數最直接的作用就是控制任務執(zhí)行順序,使同步執(zhí)行。
同時,柵欄函數需要注意一下幾點
柵欄函數只能控制同一并發(fā)隊列同步柵欄添加進入隊列的時候,當前線程會被鎖死,直到同步柵欄之前的任務和同步柵欄任務本身執(zhí)行完畢時,當前線程才會打開然后繼續(xù)執(zhí)行下一句代碼。在使用柵欄函數時.使用自定義隊列才有意義,如果用的是串行隊列或者系統提供的全局并發(fā)隊列,這個柵欄函數的作用等同于一個同步函數的作用,沒有任何意義
所以我們可以使用異步柵欄函數dispatch_barrier_async 在上面的addObject 方法中給任務添加類似依賴關系,使線程安全。
我們分析一下同步柵欄dispatch_barrier_sync源碼
void
dispatch_barrier_sync(dispatch_queue_t dq, dispatch_block_t work)
{
uintptr_t dc_flags = DC_FLAG_BARRIER | DC_FLAG_BLOCK;
if (unlikely(_dispatch_block_has_private_data(work))) {
return _dispatch_sync_block_with_privdata(dq, work, dc_flags);
}
_dispatch_barrier_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags);
}
進入_dispatch_barrier_sync_f -> _dispatch_barrier_sync_f_inline源碼
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_barrier_sync_f_inline(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func, uintptr_t dc_flags)
{
dispatch_tid tid = _dispatch_tid_self();//獲取線程的id,即線程的唯一標識
...
//判斷線程狀態(tài),需不需要等待,是否回收
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);
}
//驗證target是否存在,如果存在,加入柵欄函數的遞歸查找 是否等待
if (unlikely(dl->do_targetq->do_targetq)) {
return _dispatch_sync_recurse(dl, ctxt, func,
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)));//執(zhí)行
}
源碼主要有分為以下幾部分
通過_dispatch_tid_self獲取線程ID
通過_dispatch_queue_try_acquire_barrier_sync判斷線程狀態(tài)
進入_dispatch_queue_try_acquire_barrier_sync_and_suspend,在這里進行釋放
通過_dispatch_sync_recurse遞歸查找柵欄函數的target
通過_dispatch_introspection_sync_begin對向前信息進行處理
通過_dispatch_lane_barrier_sync_invoke_and_complete執(zhí)行block并釋放
這里可以查看上篇 dispatch_sync同步任務的源碼分析。 這里就不多做說明了。
三 、信號量 dispatch_semaphore_t 的分析
信號量的作用一般是用來使任務同步執(zhí)行,類似于互斥鎖,用戶可以根據需要控制GCD最大并發(fā)數,一般是這樣使用的
//信號量
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(sem);
注意:這兩個要成對出現
下面我們來分析其底層實現流程
dispatch_semaphore_create 創(chuàng)建
- 該函數的底層實現如下,主要是
初始化信號量,并設置GCD的最大并發(fā)數,其最大并發(fā)數必須大于0
dispatch_semaphore_t
dispatch_semaphore_create(long value)
{
dispatch_semaphore_t dsema;
// If the internal value is negative, then the absolute of the value is
// equal to the number of waiting threads. Therefore it is bogus to
// initialize the semaphore with a negative value.
//翻譯:
//如果內部值為負,則該值的絕對值為
//等于等待線程的數量。因此它是虛假的
//用一個負值初始化信號量。
if (value < 0) {
return DISPATCH_BAD_INPUT;
}
//初始化信號量
dsema = _dispatch_object_alloc(DISPATCH_VTABLE(semaphore),
sizeof(struct dispatch_semaphore_s));
dsema->do_next = DISPATCH_OBJECT_LISTLESS;
dsema->do_targetq = _dispatch_get_default_queue(false);
dsema->dsema_value = value;
_dispatch_sema4_init(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
dsema->dsema_orig = value;
return dsema;
}
dispatch_semaphore_wait 加鎖
該函數的源碼實現如下,其主要作用是對信號量dsema通過os_atomic_dec2o進行了--操作,其內部是執(zhí)行的C++的atomic_fetch_sub_explicit方法
如果value 大于
等于0,表示操作無效,即執(zhí)行成功如果value 等于
LONG_MIN,系統會拋出一個crash如果value
小于0,則進入長等待
long
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
{
// dsema_value 進行 -- 操作
long value = os_atomic_dec2o(dsema, dsema_value, acquire);
if (likely(value >= 0)) {//表示執(zhí)行操作無效,即執(zhí)行成功
return 0;
}
return _dispatch_semaphore_wait_slow(dsema, timeout);//長等待
}
其中os_atomic_dec2o的宏定義轉換如下
os_atomic_inc2o(p, f, m)
os_atomic_sub2o(p, f, 1, m).
_os_atomic_c11_op((p), (v), m, sub, -) 1, m)
_os_atomic_c11_op((p), (v), m, add, +).
({ _os_atomic_basetypeof(p) _v = (v), _r = \
atomic_fetch_##o##_explicit(_os_atomic_c11_atomic(p), _v, \
memory_order_##m); (__typeof__(_r))(_r op _v); })
將具體的值代入為
os_atomic_dec2o(dsema, dsema_value, acquire);
os_atomic_sub2o(dsema, dsema_value, 1, m)
os_atomic_sub(dsema->dsema_value, 1, m)
_os_atomic_c11_op(dsema->dsema_value, 1, m, sub, -)
_r = atomic_fetch_sub_explicit(dsema->dsema_value, 1),
等價于 dsema->dsema_value - 1
- 進入
_dispatch_semaphore_wait_slow的源碼實現,當value小于0時,根據等待事件timeout做出不同操作
static long
_dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema,
dispatch_time_t timeout)
{
long orig;
_dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
switch (timeout) {
default:
if (!_dispatch_sema4_timedwait(&dsema->dsema_sema, timeout)) {
break;
}
// Fall through and try to undo what the fast path did to
// dsema->dsema_value
//失敗并嘗試撤銷快速路徑所造成的損失
// dsema - > dsema_value
case DISPATCH_TIME_NOW:
orig = dsema->dsema_value;
while (orig < 0) {
if (os_atomic_cmpxchgvw2o(dsema, dsema_value, orig, orig + 1,
&orig, relaxed)) {
return _DSEMA4_TIMEOUT();
}
}
// Another thread called semaphore_signal().
// Fall through and drain the wakeup.
//另一個線程稱為semaphore_signal()。
//喚醒失敗
case DISPATCH_TIME_FOREVER:
_dispatch_sema4_wait(&dsema->dsema_sema);
break;
}
return 0;
}
dispatch_semaphore_signal 解鎖
該函數的源碼實現如下,其核心也是通過os_atomic_inc2o函數對value進行了++操作,os_atomic_inc2o內部是通過C++的atomic_fetch_add_explicit
如果value 大于 0,表示操作無效,即執(zhí)行成功
如果value 等于0,則進入長等待
long
dispatch_semaphore_signal(dispatch_semaphore_t dsema)
{
//signal 對 value是 ++
long value = os_atomic_inc2o(dsema, dsema_value, release);
if (likely(value > 0)) {//返回0,表示當前的執(zhí)行操作無效,相當于執(zhí)行成功
return 0;
}
if (unlikely(value == LONG_MIN)) {
DISPATCH_CLIENT_CRASH(value,
"Unbalanced call to dispatch_semaphore_signal()");
}
return _dispatch_semaphore_signal_slow(dsema);//進入長等待
}
其中os_atomic_dec2o的宏定義轉換如下
os_atomic_inc2o(p, f, m)
os_atomic_add2o(p, f, 1, m)
os_atomic_add(&(p)->f, (v), m)
_os_atomic_c11_op((p), (v), m, add, +)
({ _os_atomic_basetypeof(p) _v = (v), _r = \
atomic_fetch_##o##_explicit(_os_atomic_c11_atomic(p), _v, \
memory_order_##m); (__typeof__(_r))(_r op _v); })
將具體的值代入為
os_atomic_inc2o(dsema, dsema_value, release);
os_atomic_add2o(dsema, dsema_value, 1, m)
os_atomic_add(&(dsema)->dsema_value, (1), m)
_os_atomic_c11_op((dsema->dsema_value), (1), m, add, +)
_r = atomic_fetch_add_explicit(dsema->dsema_value, 1),
等價于 dsema->dsema_value + 1
總結
dispatch_semaphore_create主要就是初始化限號量dispatch_semaphore_wait是對信號量的value進行--,即加鎖操作dispatch_semaphore_signal是對信號量的value進行++,即解鎖操作
所以,綜上所述,信號量相關函數的底層操作如圖所示

四 、調度組 dispatch_group_ 的分析
調度組的最直接作用是控制任務執(zhí)行順序,常見操作如下
dispatch_group_create 創(chuàng)建組
dispatch_group_async 進組任務
dispatch_group_notify 進組任務執(zhí)行完畢通知
dispatch_group_wait 暫停當前線程(阻塞當前線程),等待指定的 group 中的任務執(zhí)行完成后,才會往下繼續(xù)執(zhí)行
//進組和出組一般是`成對使用`的
dispatch_group_enter標志著一個任務追加到 group,執(zhí)行一次,相當于group 中未執(zhí)行完畢任務數+1
dispatch_group_leave標志著一個任務離開了 group,執(zhí)行一次,相當于 group 中未執(zhí)行完畢任務數-1。
dispatch_group_create 創(chuàng)建組
主要是創(chuàng)建group,并設置屬性,此時的group的value為0
- 進入
dispatch_group_create源碼
dispatch_group_t
dispatch_group_create(void)
{
return _dispatch_group_create_with_count(0);
}
- 進入
_dispatch_group_create_with_count源碼,其中是對group對象屬性賦值,并返回group對象,其中的n等于0
DISPATCH_ALWAYS_INLINE
static inline dispatch_group_t
_dispatch_group_create_with_count(uint32_t n)
{
//創(chuàng)建group對象,類型為OS_dispatch_group
dispatch_group_t dg = _dispatch_object_alloc(DISPATCH_VTABLE(group),
sizeof(struct dispatch_group_s));
//group對象賦值
dg->do_next = DISPATCH_OBJECT_LISTLESS;
dg->do_targetq = _dispatch_get_default_queue(false);
if (n) {
os_atomic_store2o(dg, dg_bits,
(uint32_t)-n * DISPATCH_GROUP_VALUE_INTERVAL, relaxed);
os_atomic_store2o(dg, do_ref_cnt, 1, relaxed); // <rdar://22318411>
}
return dg;
}
dispatch_group_enter 進組
進入dispatch_group_enter源碼,通過os_atomic_sub_orig2o對dg->dg.bits 作--操作,對數值進行處理
void
dispatch_group_enter(dispatch_group_t dg)
{
// The value is decremented on a 32bits wide atomic so that the carry
// for the 0 -> -1 transition is not propagated to the upper 32bits.
uint32_t old_bits = os_atomic_sub_orig2o(dg, dg_bits,//原子遞減 0 -> -1
DISPATCH_GROUP_VALUE_INTERVAL, acquire);
uint32_t old_value = old_bits & DISPATCH_GROUP_VALUE_MASK;
if (unlikely(old_value == 0)) {//如果old_value
_dispatch_retain(dg); // <rdar://problem/22318411>
}
if (unlikely(old_value == DISPATCH_GROUP_VALUE_MAX)) {//到達臨界值,會報crash
DISPATCH_CLIENT_CRASH(old_bits,
"Too many nested calls to dispatch_group_enter()");
}
}
dispatch_group_leave 出組
進入dispatch_group_leave源碼
-1 到 0,即++操作
根據狀態(tài),do-while循環(huán),喚醒執(zhí)行block任務
如果0 + 1 = 1,enter-leave不平衡,即leave多次調用,會crash
void
dispatch_group_leave(dispatch_group_t dg)
{
// The value is incremented on a 64bits wide atomic so that the carry for
// the -1 -> 0 transition increments the generation atomically.
uint64_t new_state, old_state = os_atomic_add_orig2o(dg, dg_state,//原子遞增 ++
DISPATCH_GROUP_VALUE_INTERVAL, release);
uint32_t old_value = (uint32_t)(old_state & DISPATCH_GROUP_VALUE_MASK);
//根據狀態(tài),喚醒
if (unlikely(old_value == DISPATCH_GROUP_VALUE_1)) {
old_state += DISPATCH_GROUP_VALUE_INTERVAL;
do {
new_state = old_state;
if ((old_state & DISPATCH_GROUP_VALUE_MASK) == 0) {
new_state &= ~DISPATCH_GROUP_HAS_WAITERS;
new_state &= ~DISPATCH_GROUP_HAS_NOTIFS;
} else {
// If the group was entered again since the atomic_add above,
// we can't clear the waiters bit anymore as we don't know for
// which generation the waiters are for
new_state &= ~DISPATCH_GROUP_HAS_NOTIFS;
}
if (old_state == new_state) break;
} while (unlikely(!os_atomic_cmpxchgv2o(dg, dg_state,
old_state, new_state, &old_state, relaxed)));
return _dispatch_group_wake(dg, old_state, true);//喚醒
}
//-1 -> 0, 0+1 -> 1,即多次leave,會報crash,簡單來說就是enter-leave不平衡
if (unlikely(old_value == 0)) {
DISPATCH_CLIENT_CRASH((uintptr_t)old_value,
"Unbalanced call to dispatch_group_leave()");
}
}
- 進入
_dispatch_group_wake源碼,do-while循環(huán)進行異步命中,調用_dispatch_continuation_async執(zhí)行
DISPATCH_NOINLINE
static void
_dispatch_group_wake(dispatch_group_t dg, uint64_t dg_state, bool needs_release)
{
uint16_t refs = needs_release ? 1 : 0; // <rdar://problem/22318411>
if (dg_state & DISPATCH_GROUP_HAS_NOTIFS) {
dispatch_continuation_t dc, next_dc, tail;
// Snapshot before anything is notified/woken <rdar://problem/8554546>
dc = os_mpsc_capture_snapshot(os_mpsc(dg, dg_notify), &tail);
do {
dispatch_queue_t dsn_queue = (dispatch_queue_t)dc->dc_data;
next_dc = os_mpsc_pop_snapshot_head(dc, tail, do_next);
_dispatch_continuation_async(dsn_queue, dc,
_dispatch_qos_from_pp(dc->dc_priority), dc->dc_flags);//block任務執(zhí)行
_dispatch_release(dsn_queue);
} while ((dc = next_dc));//do-while循環(huán),進行異步任務的命中
refs++;
}
if (dg_state & DISPATCH_GROUP_HAS_WAITERS) {
_dispatch_wake_by_address(&dg->dg_gen);//地址釋放
}
if (refs) _dispatch_release_n(dg, refs);//引用釋放
}
- 進入
_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);//與dx_invoke一樣,都是宏
}
這步與異步函數的block回調執(zhí)行是一致的,這里不再作說明
dispatch_group_notify 通知
進入dispatch_group_notify源碼,如果old_state等于0,就可以進行釋放了
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_group_notify(dispatch_group_t dg, dispatch_queue_t dq,
dispatch_continuation_t dsn)
{
uint64_t old_state, new_state;
dispatch_continuation_t prev;
dsn->dc_data = dq;
_dispatch_retain(dq);
//獲取dg底層的狀態(tài)標識碼,通過os_atomic_store2o獲取的值,即從dg的狀態(tài)碼 轉成了 os底層的state
prev = os_mpsc_push_update_tail(os_mpsc(dg, dg_notify), dsn, do_next);
if (os_mpsc_push_was_empty(prev)) _dispatch_retain(dg);
os_mpsc_push_update_prev(os_mpsc(dg, dg_notify), prev, dsn, do_next);
if (os_mpsc_push_was_empty(prev)) {
os_atomic_rmw_loop2o(dg, dg_state, old_state, new_state, release, {
new_state = old_state | DISPATCH_GROUP_HAS_NOTIFS;
if ((uint32_t)old_state == 0) { //如果等于0,則可以進行釋放了
os_atomic_rmw_loop_give_up({
return _dispatch_group_wake(dg, new_state, false);//喚醒
});
}
});
}
}
除了leave可以通過_dispatch_group_wake喚醒,其中dispatch_group_notify也是可以喚醒的
其中os_mpsc_push_update_tail是宏定義,用于獲取dg的狀態(tài)碼
#define os_mpsc_push_update_tail(Q, tail, _o_next) ({ \
os_mpsc_node_type(Q) _tl = (tail); \
os_atomic_store2o(_tl, _o_next, NULL, relaxed); \
os_atomic_xchg(_os_mpsc_tail Q, _tl, release); \
})
dispatch_group_async
進入dispatch_group_async 源碼,主要是包裝任務和異步處理任務
#ifdef __BLOCKS__
void
dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
dispatch_block_t db)
{
dispatch_continuation_t dc = _dispatch_continuation_alloc();
uintptr_t dc_flags = DC_FLAG_CONSUME | DC_FLAG_GROUP_ASYNC;
dispatch_qos_t qos;
//任務包裝器
qos = _dispatch_continuation_init(dc, dq, db, 0, dc_flags);
//處理任務
_dispatch_continuation_group_async(dg, dq, dc, qos);
}
#endif
進入_dispatch_continuation_group_async源碼,主要是封裝了dispatch_group_enter進組操作
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_continuation_group_async(dispatch_group_t dg, dispatch_queue_t dq,
dispatch_continuation_t dc, dispatch_qos_t qos)
{
dispatch_group_enter(dg);//進組
dc->dc_data = dg;
_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);//異步操作
}
dispatch_group_enter 要和dispatch_group_leave 成對出現,所以我們看看dispatch_group_async在哪里調用leave操作,堆棧調試如下:

在_dispatch_continuation_with_group_invoke中
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_continuation_with_group_invoke(dispatch_continuation_t dc)
{
struct dispatch_object_s *dou = dc->dc_data;
unsigned long type = dx_type(dou);
if (type == DISPATCH_GROUP_TYPE) {//如果是調度組類型
_dispatch_client_callout(dc->dc_ctxt, dc->dc_func);//block回調
_dispatch_trace_item_complete(dc);
dispatch_group_leave((dispatch_group_t)dou);//出組
} else {
DISPATCH_INTERNAL_CRASH(dx_type(dou), "Unexpected object type");
}
可以看出dispatch_group_async底層封裝的是enter-leave
調度組的底層分析流程如下圖所示:

五 、總結
至此GCD,常用的API,底層流程分析已經差不多分析,其實還有很多不清晰的地方,以后再慢慢研究吧。
頭疼休息一會兒.....
