前面2篇我們了解了dispatch_queue_t的數(shù)據(jù)結(jié)構(gòu)和main queue司训、global queue葵陵、user queue之間的參數(shù)差別和dispatch_sync()的實(shí)現(xiàn)甜孤,dispatch_sync
這里的fastpath(x)和slowpath(x)就相當(dāng)于x碉纳,只是加了cpu指令優(yōu)化惶楼,所以if(slowpath(x))相當(dāng)于if(x)
dispatch_sync()
https://opensource.apple.com/tarballs/libdispatch/
void
dispatch_sync(dispatch_queue_t dq, void (^work)(void))
{
#if DISPATCH_COCOA_COMPAT
//osx下GC垃圾回收處理轮蜕,最后都會(huì)調(diào)用 dispatch_sync_f
if (slowpath(dq == &_dispatch_main_q)) {
return _dispatch_sync_slow(dq, work);
}
#endif
struct Block_basic *bb = (void *)work;
//dispatch_sync最終是調(diào)用下面一行
dispatch_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke);
}
//block轉(zhuǎn)換成block結(jié)構(gòu)體和調(diào)用函數(shù)指針
void
dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func)
{
//1、如果是串行queue收壕,main queue和用戶創(chuàng)建的sereal queue
if (fastpath(dq->dq_width == 1)) {
return dispatch_barrier_sync_f(dq, ctxt, func);
}
//2妓灌、如果do_targetq不存在轨蛤,說明是global queue
if (slowpath(!dq->do_targetq)) {
// the global root queues do not need strict ordering
(void)dispatch_atomic_add2o(dq, dq_running, 2);
return _dispatch_sync_f_invoke(dq, ctxt, func);
}
//3、用戶創(chuàng)建的concurrent queue
_dispatch_sync_f2(dq, ctxt, func);
}
1)虫埂、dispatch_barrier_sync_f :main queue 或 用戶創(chuàng)建的 serial queue
void
dispatch_barrier_sync_f(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func)
{
// 1) ensure that this thread hasn't enqueued anything ahead of this call
// 2) the queue is not suspended
//1俱萍、如果queue有任務(wù)在等待執(zhí)行或已經(jīng)暫停,則把任務(wù)放到鏈表末尾告丢,并使用信號(hào)等待進(jìn)行同步枪蘑,發(fā)送一個(gè)完成信號(hào)
if (slowpath(dq->dq_items_tail) || slowpath(DISPATCH_OBJECT_SUSPENDED(dq))){
return _dispatch_barrier_sync_f_slow(dq, ctxt, func);
}
//2、如果dq->dq_running==0 則dq->dq_running=1岖免,并返回true岳颇,否則返回false
if (slowpath(!dispatch_atomic_cmpxchg2o(dq, dq_running, 0, 1))) {
// global queues and main queue bound to main thread always falls into
// the slow case
//3、把任務(wù)放到鏈表末尾颅湘,并使用信號(hào)等待進(jìn)行同步话侧,發(fā)送一個(gè)完成信號(hào)
return _dispatch_barrier_sync_f_slow(dq, ctxt, func);
}
//如果dq->do_targetq->do_targetq存在,將轉(zhuǎn)成到do_targetq去執(zhí)行
if (slowpath(dq->do_targetq->do_targetq)) {
return _dispatch_barrier_sync_f_recurse(dq, ctxt, func);
}
//最后直接調(diào)用_dispatch_function_invoke執(zhí)行block闯参,并發(fā)送一個(gè)信號(hào)量
_dispatch_barrier_sync_f_invoke(dq, ctxt, func);
}
//這個(gè)方法是 把任務(wù)放到鏈表末尾瞻鹏,并使用信號(hào)等待進(jìn)行同步
static void
_dispatch_barrier_sync_f_slow(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func)
{
//封裝一個(gè)結(jié)構(gòu)體包含block,block的函數(shù)指針鹿寨,dbss2_sema信號(hào)同步
struct dispatch_barrier_sync_slow2_s dbss2 = {
.dbss2_dq = dq,
#if DISPATCH_COCOA_COMPAT
.dbss2_func = func,
.dbss2_ctxt = ctxt,
#endif
.dbss2_sema = _dispatch_get_thread_semaphore(),
};
struct dispatch_barrier_sync_slow_s dbss = {
.do_vtable = (void *)(DISPATCH_OBJ_BARRIER_BIT |
DISPATCH_OBJ_SYNC_SLOW_BIT),
.dc_func = _dispatch_barrier_sync_f_slow_invoke,
.dc_ctxt = &dbss2,
};
//將帶有同步信號(hào)的任務(wù)添加到queue的任務(wù)列表新博,添加任務(wù)列表上一篇講了
_dispatch_queue_push(dq, (void *)&dbss);
//等待方法完成并調(diào)用信息量
_dispatch_thread_semaphore_wait(dbss2.dbss2_sema);
}
//這個(gè)方法是直接調(diào)用block,并且如果queue有未執(zhí)行的則發(fā)送一個(gè)信號(hào)量
static void
_dispatch_barrier_sync_f_invoke(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func)
{
dispatch_atomic_acquire_barrier();
_dispatch_function_invoke(dq, ctxt, func);
dispatch_atomic_release_barrier();
if (slowpath(dq->dq_items_tail)) {
return _dispatch_barrier_sync_f2(dq);
}
if (slowpath(dispatch_atomic_dec2o(dq, dq_running) == 0)) {
_dispatch_wakeup(dq);
}
}
//_dispatch_function_invoke最終調(diào)用下面的直接調(diào)用block
void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
return f(ctxt);
}
2)脚草、_dispatch_sync_f_invoke:global queue
//global queue由于不需要先后順序赫悄,直接dq_running+=2,表示運(yùn)行狀態(tài)馏慨,
//然后直接調(diào)用_dispatch_function_invoke來調(diào)用block埂淮,再dq_running-=2
static void
_dispatch_sync_f_invoke(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func)
{
_dispatch_function_invoke(dq, ctxt, func);
if (slowpath(dispatch_atomic_sub2o(dq, dq_running, 2) == 0)) {
_dispatch_wakeup(dq);
}
}
3)、_dispatch_sync_f2:用戶創(chuàng)建的concurrent queue写隶,并行
static void
_dispatch_sync_f2(dispatch_queue_t dq, void *ctxt, dispatch_function_t func)
{
// 1) ensure that this thread hasn't enqueued anything ahead of this call
// 2) the queue is not suspended
//1倔撞、如果queue有任務(wù)在等待執(zhí)行
if (slowpath(dq->dq_items_tail) || slowpath(DISPATCH_OBJECT_SUSPENDED(dq))){
//這個(gè)函數(shù)內(nèi)部會(huì)用一個(gè)semaphore等待信號(hào),然后調(diào)用_dispatch_function_invoke直接在本線程調(diào)用block
return _dispatch_sync_f_slow(dq, ctxt, func);
}
//2慕趴、如果queue處于暫停狀態(tài)
if (slowpath(dispatch_atomic_add2o(dq, dq_running, 2) & 1)) {
//這個(gè)函數(shù)內(nèi)部先喚醒隊(duì)列痪蝇,然后用一個(gè)semaphore等待信號(hào),然后調(diào)用_dispatch_function_invoke直接在本線程調(diào)用block
return _dispatch_sync_f_slow2(dq, ctxt, func);
}
//如果dq->do_targetq->do_targetq存在秩贰,將轉(zhuǎn)成到do_targetq去執(zhí)行
if (slowpath(dq->do_targetq->do_targetq)) {
return _dispatch_sync_f_recurse(dq, ctxt, func);
}
//最后直接調(diào)用_dispatch_function_invoke在本線程執(zhí)行block霹俺,
_dispatch_sync_f_invoke(dq, ctxt, func);
}
這個(gè)有沒有感覺似曾相識(shí)柔吼,沒錯(cuò)毒费,這個(gè)跟1)差不多,唯一不同的是1)做完了后發(fā)了個(gè)完成信號(hào)
總結(jié):
1愈魏、如果是global queue觅玻,不用管順序想际,直接在當(dāng)前線程調(diào)用block,這個(gè)是最簡(jiǎn)單的
2溪厘、如果是用戶創(chuàng)建的并行queue胡本,如果有任務(wù)在等待或queue已經(jīng)暫停,先喚醒quque畸悬,然后用一個(gè)semaphore信號(hào)等待開始執(zhí)行信號(hào)侧甫,最后調(diào)用_dispatch_function_invoke在本線程執(zhí)行block,
3蹋宦、如果是main queue或用戶創(chuàng)建的串行queue披粟,則queue有任務(wù)等待執(zhí)行就把當(dāng)前任務(wù)放到任務(wù)鏈表末尾,并用信號(hào)等待同步冷冗,在本線程調(diào)用block守屉,如果沒有任務(wù)在等待執(zhí)行,則直接在本線程調(diào)用block蒿辙,但這個(gè)最后都有一個(gè)步驟拇泛,發(fā)送一個(gè)完成信號(hào)量