上一篇我們了解了dispatch_queue_t的數(shù)據(jù)結(jié)構(gòu)和main queue、global queue缤削、user queue之間的參數(shù)差別,這一章我們來分析下GCD的方法
這里的fastpath(x)和slowpath(x)就相當(dāng)于x,只是加了cpu指令優(yōu)化蛛枚,所以if(slowpath(x))相當(dāng)于if(x)
dispatch_async()
https://opensource.apple.com/tarballs/libdispatch/
void
dispatch_async(dispatch_queue_t dq, void (^work)(void))
{
dispatch_async_f(dq, _dispatch_Block_copy(work),
_dispatch_call_block_and_release);
}
//調(diào)用了下面的方法
void
dispatch_async_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func)
{
//1、定義一個封裝的block操作
dispatch_continuation_t dc;
// No fastpath/slowpath hint because we simply don't know
//2脸哀、dq_width == 1表示串行蹦浦,就是main queue和用戶創(chuàng)建的串行queue
if (dq->dq_width == 1) {
return dispatch_barrier_async_f(dq, ctxt, func);
}
//如果是global queue和用戶創(chuàng)建的并行queue則繼續(xù)向下走
//3、線程中有個dispatch_continuation_t緩存鏈表撞蜂,如果獲取到就把鏈表的下一個設(shè)為緩存盲镶,相當(dāng)于把第一個取出來了
dc = fastpath(_dispatch_continuation_alloc_cacheonly());
if (!dc) {
//4、如果沒有dispatch_continuation_t緩存在堆上創(chuàng)建一個蝌诡,并且初始化后調(diào)用_dispatch_queue_push
return _dispatch_async_f_slow(dq, ctxt, func);
}
//5溉贿、初始化dispatch_continuation_t,把block封裝成dispatch_continuation_t
dc->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT;
dc->dc_func = func;
dc->dc_ctxt = ctxt;
// No fastpath/slowpath hint because we simply don't know
//6浦旱、如果do_targetq存在宇色,則任務(wù)有do_targetq來執(zhí)行
if (dq->do_targetq) {
return _dispatch_async_f2(dq, dc);
}
//7、把dispatch_continuation_t放到queue的執(zhí)行列表中
_dispatch_queue_push(dq, dc);
}
//dispatch_continuation_t的結(jié)構(gòu),封裝的一個block操作
struct dispatch_continuation_s {
const void *do_vtable;
struct dispatch_continuation_s *volatile do_next;
dispatch_function_t dc_func;
void *dc_ctxt
dispatch_group_t dc_group;
void *dc_data[3];
};
通過上面的方法我們可以看出宣蠕,dispatch_async_f最終將block封裝成dispatch_continuation_s并調(diào)用_dispatch_queue_push放到對應(yīng)的queue的執(zhí)行鏈表結(jié)尾例隆,global queue是放到自己的執(zhí)行鏈表執(zhí)行,main queue和user queue放到do_targetq的執(zhí)行鏈表執(zhí)行
下面我們再來分析_dispatch_queue_push()方法
#define _dispatch_queue_push(x, y) _dispatch_queue_push_list((x), (y), (y))
#define _dispatch_queue_push_list _dispatch_trace_queue_push_list
//最終是調(diào)用了下面的方法
static inline void
_dispatch_trace_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head,
dispatch_object_t _tail)
{
if (slowpath(DISPATCH_QUEUE_PUSH_ENABLED())) {
struct dispatch_object_s *dou = _head._do;
do {
//如果_head和_tail不相同時進行處理
_dispatch_trace_continuation(dq, dou, DISPATCH_QUEUE_PUSH);
} while (dou != _tail._do && (dou = dou->do_next));
}
//最后調(diào)用這個方法把封裝的操作放到queue的執(zhí)行鏈表
_dispatch_queue_push_list(dq, _head, _tail);
}
//真正放入鏈表的方法
static inline void
_dispatch_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head,
dispatch_object_t _tail)
{
struct dispatch_object_s *prev, *head = _head._do, *tail = _tail._do;
tail->do_next = NULL;
dispatch_atomic_store_barrier();
//把dq->dq_items_tail與tail交換并把之前的值返回
prev = fastpath(dispatch_atomic_xchg2o(dq, dq_items_tail, tail));
//如果queue中存在還未執(zhí)行的鏈表植影,則把push的鏈表頭接到原來的尾部
if (prev) {
// if we crash here with a value less than 0x1000, then we are at a
// known bug in client code for example, see _dispatch_queue_dispose
// or _dispatch_atfork_child
prev->do_next = head;
} else {
_dispatch_queue_push_list_slow(dq, head);
}
}
//如果queue中沒有未完成的任務(wù)裳擎,則push進來的鏈表頭直接設(shè)置成queue的待執(zhí)行鏈表頭,并且喚醒queue的線程
void
_dispatch_queue_push_list_slow(dispatch_queue_t dq,
struct dispatch_object_s *obj)
{
// The queue must be retained before dq_items_head is written in order
// to ensure that the reference is still valid when _dispatch_wakeup is
// called. Otherwise, if preempted between the assignment to
// dq_items_head and _dispatch_wakeup, the blocks submitted to the
// queue may release the last reference to the queue when invoked by
// _dispatch_queue_drain. <rdar://problem/6932776>
_dispatch_retain(dq);
dq->dq_items_head = obj;
_dispatch_wakeup(dq);
_dispatch_release(dq);
}
dispatch_async()總結(jié):
1思币、先將block封裝成dispatch_continuation_s結(jié)構(gòu)體
2鹿响、如果目標(biāo)queue的do_targetq不存在,則把封裝的dispatch_continuation_s插入到目標(biāo)待執(zhí)行鏈表尾部
3谷饿、如果目標(biāo)queue的do_targetq存在惶我,則把封裝的dispatch_continuation_s出入到do_targetq的待執(zhí)行鏈表尾部
4、如果queue的待執(zhí)行鏈表存在博投,一般情況下說明queue正在運行绸贡,直接插入新的dispatch_continuation_s就可以
5、如果queue的待執(zhí)行鏈表不存在毅哗,說明queue是沒有任務(wù)暫停狀態(tài)的听怕,把dispatch_continuation_s插入鏈表后喚醒queue的線程