GCD源碼解析
在我們開(kāi)發(fā)過(guò)程中煤伟,我們使用到多線程的時(shí)候,最多的應(yīng)該是使用GCD,因?yàn)樗褂闷饋?lái)非常的簡(jiǎn)潔便锨,簡(jiǎn)單围辙。
平常中,我們使用最多的放案,
dispatch_queue_create
,
dispatch_get_global_queue
,
dispatch_sync
,
dispatch_async
,
dispatch_group_create
,
dispatch_once
等姚建。可是吱殉,我們是否知道這些背后是做了什么事情掸冤,它和我們的內(nèi)部線程到底是什么關(guān)系,或者友雳,他們直接是否有直接的關(guān)系稿湿?我依次來(lái)做解答
dispatch_sync
dispatch_sync
和dispatch_async
是相對(duì)的,一個(gè)是同步任務(wù)押赊,需要等待任務(wù)完成饺藤,一個(gè)異步不等待任務(wù)完成就返回。兩種不同的方式流礁,首先我們先介紹dispatch_sync
.
dispatch_sync
: 它一般情況下是不會(huì)重啟一個(gè)新的線程涕俗,而是在當(dāng)前
線程執(zhí)行,注意神帅,這里說(shuō)的是當(dāng)前線程再姑,而不是主線程。當(dāng)然里面有特殊的存在get_main_queue
,它是在主線程執(zhí)行的找御。當(dāng)我們調(diào)用dispatch_sync
元镀,也就是同步任務(wù)的時(shí)候,是分兩種隊(duì)列的萎坷,一種就是串行隊(duì)列凹联,一種是并發(fā)隊(duì)列。在我們解析dispatch_sync
源碼的之前哆档,我們先寫(xiě)幾個(gè)例子,并且住闯,把調(diào)用棧給寫(xiě)出來(lái)瓜浸,這樣更加有利于我們分析源碼。
// 串行隊(duì)列同步執(zhí)行
dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_SERIAL);//創(chuàng)建的是串行隊(duì)列
dispatch_sync(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"111");
});
dispatch_sync(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"222");
});
// 下面是運(yùn)行結(jié)果
myDemo[41725:1739115] current thread = <NSThread: 0x60400006cb80>{number = 1, name = main}
myDemo[41725:1739115] 111
myDemo[41725:1739115] current thread = <NSThread: 0x60400006cb80>{number = 1, name = main}
myDemo[41725:1739115] 222
首先我們把此demo的堆棧貼出來(lái)
通過(guò)這個(gè)demo比原,就是按照我們所理解的串行執(zhí)行插佛,線程也是在主線程,因?yàn)槲覀儽旧砭褪悄J(rèn)在主線程運(yùn)行量窘,就像上面講的:dispatch_sync
只是在當(dāng)前線程執(zhí)行
// 并行隊(duì)列的同步執(zhí)行
dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"111");
[NSThread sleepForTimeInterval:5];
});
dispatch_sync(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"222");
});
// 運(yùn)行結(jié)果
myDemo[82272:3241865] current thread = <NSThread: 0x60000006f900>{number = 1, name = main}
myDemo[82272:3241865] 111
myDemo[82272:3241865] current thread = <NSThread: 0x60000006f900>{number = 1, name = main}
myDemo[82272:3241865] 222
堆棧隊(duì)列
通過(guò)這個(gè)demo雇寇,我可以看到,即便是用的并行隊(duì)列,依然還是在主線程锨侯,并沒(méi)有重啟一個(gè)新的線程嫩海,并且是串行執(zhí)行。
說(shuō)明囚痴,我一開(kāi)始總結(jié)的是對(duì)的叁怪,執(zhí)行dispatch_sync,并沒(méi)有重啟線程深滚,即便是并行隊(duì)列奕谭。下面再看其他的demo
dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_SERIAL);// 創(chuàng)建串行隊(duì)列
dispatch_sync(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"111");
dispatch_sync(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"222");
});
});
// 此處就會(huì)死鎖造成crash.
// 我們來(lái)看另外一個(gè)例子
dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_CONCURRENT);// 創(chuàng)建并行隊(duì)列
dispatch_sync(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"111");
dispatch_sync(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"222");
});
});
// 執(zhí)行結(jié)果
myDemo[84905:3394929] current thread = <NSThread: 0x600000262e00>{number = 1, name = main}
myDemo[84905:3394929] 111
myDemo[84905:3394929] current thread = <NSThread: 0x600000262e00>{number = 1, name = main}
myDemo[84905:3394929] 222
// 執(zhí)行結(jié)果依然在主線程
通過(guò)這兩個(gè)例子的比較,只有在同步執(zhí)行痴荐,串行隊(duì)列的情況下血柳,才會(huì)造成死鎖。使用并行隊(duì)列的時(shí)候生兆,同步執(zhí)行难捌,依然沒(méi)問(wèn)題,并且依然在主線程皂贩。下面栖榨,我們上面那個(gè)例子的堆棧信息截屏下來(lái)
// 不是通過(guò)create queue的方式,創(chuàng)建全局的并行隊(duì)列
dispatch_queue_t queue01 = dispatch_get_global_queue(0, 0);
dispatch_sync(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"222");
});
// 執(zhí)行結(jié)果
myDemo[1637:4224206] current thread = <NSThread: 0x604000076700>{number = 1, name = main}
myDemo[1637:4224206] 222
這種方式執(zhí)行的是_dispatch_sync_function_invoke
堆棧:
我根據(jù)上面3中堆棧信息明刷,來(lái)分析dispatch_sync
源碼執(zhí)行婴栽,解答為什么如此執(zhí)行
dispatch_sync(dispatch_queue_t dq, dispatch_block_t work)
{
if (unlikely(_dispatch_block_has_private_data(work))) {
return _dispatch_sync_block_with_private_data(dq, work, 0);
}
dispatch_sync_f(dq, work, _dispatch_Block_invoke(work));
}
DISPATCH_NOINLINE
void
dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func)
{
if (likely(dq->dq_width == 1)) {
// 串行隊(duì)列執(zhí)行到這里
return dispatch_barrier_sync_f(dq, ctxt, func);
}
// 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(dq))) {
// 通過(guò)下面的堆棧,當(dāng)創(chuàng)建全局并行隊(duì)列的時(shí)候辈末,才會(huì)執(zhí)行到此方法
return _dispatch_sync_f_slow(dq, ctxt, func, 0);
}
_dispatch_introspection_sync_begin(dq);
if (unlikely(dq->do_targetq->do_targetq)) {
return _dispatch_sync_recurse(dq, ctxt, func, 0);
}
// 通過(guò)上面并行隊(duì)列堆棧發(fā)現(xiàn)dispatch_queue_create創(chuàng)建的并且隊(duì)列愚争,會(huì)調(diào)用此方法
_dispatch_sync_invoke_and_complete(dq, ctxt, func);
}
DISPATCH_NOINLINE
void
dispatch_barrier_sync_f(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func)
{
dispatch_tid tid = _dispatch_tid_self();
// The more correct thing to do would be to merge the qos of the thread
// that just acquired the barrier lock into the queue state.
//
// However this is too expensive for the fastpath, so skip doing it.
// The chosen tradeoff is that if an enqueue on a lower priority thread
// contends with this fastpath, this thread may receive a useless override.
//
// 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_acquire_barrier_sync(dq, tid))) {
return _dispatch_sync_f_slow(dq, ctxt, func, DISPATCH_OBJ_BARRIER_BIT);
}
_dispatch_introspection_sync_begin(dq);
if (unlikely(dq->do_targetq->do_targetq)) {
return _dispatch_sync_recurse(dq, ctxt, func, DISPATCH_OBJ_BARRIER_BIT);
}
// 串行隊(duì)列的時(shí)候,執(zhí)行此方法
_dispatch_queue_barrier_sync_invoke_and_complete(dq, ctxt, func);
}
通過(guò)上面串行隊(duì)列的堆棧挤聘,我看到執(zhí)行到此方法_dispatch_queue_barrier_sync_invoke_and_complete
DISPATCH_NOINLINE
static void
_dispatch_queue_barrier_sync_invoke_and_complete(dispatch_queue_t dq,
void *ctxt, dispatch_function_t func)
{
// 首先先執(zhí)行此方法
_dispatch_sync_function_invoke_inline(dq, ctxt, func);
if (unlikely(dq->dq_items_tail || dq->dq_width > 1)) {
return _dispatch_queue_barrier_complete(dq, 0, 0);
}
// Presence of any of these bits requires more work that only
// _dispatch_queue_barrier_complete() handles properly
//
// Note: testing for RECEIVED_OVERRIDE or RECEIVED_SYNC_WAIT without
// checking the role is sloppy, but is a super fast check, and neither of
// these bits should be set if the lock was never contended/discovered.
const uint64_t fail_unlock_mask = DISPATCH_QUEUE_SUSPEND_BITS_MASK |
DISPATCH_QUEUE_ENQUEUED | DISPATCH_QUEUE_DIRTY |
DISPATCH_QUEUE_RECEIVED_OVERRIDE | DISPATCH_QUEUE_SYNC_TRANSFER |
DISPATCH_QUEUE_RECEIVED_SYNC_WAIT;
uint64_t old_state, new_state;
// similar to _dispatch_queue_drain_try_unlock
os_atomic_rmw_loop2o(dq, dq_state, old_state, new_state, release, {
new_state = old_state - DISPATCH_QUEUE_SERIAL_DRAIN_OWNED;
new_state &= ~DISPATCH_QUEUE_DRAIN_UNLOCK_MASK;
new_state &= ~DISPATCH_QUEUE_MAX_QOS_MASK;
if (unlikely(old_state & fail_unlock_mask)) {
os_atomic_rmw_loop_give_up({
return _dispatch_queue_barrier_complete(dq, 0, 0);
});
}
});
if (_dq_state_is_base_wlh(old_state)) {
_dispatch_event_loop_assert_not_owned((dispatch_wlh_t)dq);
}
}
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_sync_function_invoke_inline(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func)
{
dispatch_thread_frame_s dtf;
_dispatch_thread_frame_push(&dtf, dq);
// 在我們的串行隊(duì)列中轰枝,最終執(zhí)行到此方法
_dispatch_client_callout(ctxt, func);
_dispatch_perfmon_workitem_inc();
_dispatch_thread_frame_pop(&dtf);
}
并行隊(duì)列串行執(zhí)行的時(shí)候,會(huì)執(zhí)行_dispatch_sync_invoke_and_complete
DISPATCH_NOINLINE
static void
_dispatch_sync_invoke_and_complete(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func)
{
_dispatch_sync_function_invoke_inline(dq, ctxt, func);
_dispatch_queue_non_barrier_complete(dq);
}
最終依然會(huì)執(zhí)行到_dispatch_client_callout
通過(guò)上面的源碼的分析组去,在執(zhí)行dispatch_sync
的時(shí)候鞍陨,并不會(huì)創(chuàng)建新的線程。
dispatch_async
dispatch_async
運(yùn)用了底層線程池从隆,會(huì)在和當(dāng)前線程不同的線程上處理任務(wù)诚撵。同一個(gè)串行隊(duì)列在異步執(zhí)行,會(huì)在同一個(gè)線程中键闺。不同的串行隊(duì)列寿烟,異步執(zhí)行,才會(huì)重啟一個(gè)線程辛燥。但是并行隊(duì)列筛武,每次都會(huì)重啟一個(gè)線程缝其。
首先,我依然還是通過(guò)幾個(gè)例子徘六,還看看什么效果内边,并且調(diào)出他們的堆棧.
// 串行隊(duì)列異步執(zhí)行
dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"111");
});
// 執(zhí)行結(jié)果
myDemo[38842:5876694] current thread = <NSThread: 0x600000278b40>{number = 3, name = (null)}
myDemo[38842:5876694] 111
調(diào)用堆棧:
// 異步隊(duì)列異步執(zhí)行
dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"111");
});
// 執(zhí)行結(jié)果
myDemo[39184:5895013] current thread = <NSThread: 0x604000475780>{number = 3, name = (null)}
myDemo[39184:5895013] 111
調(diào)用堆棧:
// 創(chuàng)建全局異步隊(duì)列
dispatch_queue_t queue01 = dispatch_get_global_queue(0, 0);
dispatch_async(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"111");
});
// 執(zhí)行結(jié)果
myDemo[39245:5898319] current thread = <NSThread: 0x600000466640>{number = 3, name = (null)}
myDemo[39245:5898319] 111
調(diào)用堆棧:
通過(guò)上面的代碼顯示,無(wú)論是我們創(chuàng)建的時(shí)候串行隊(duì)列硕噩,還是異步隊(duì)列假残,或者是全局隊(duì)列,只要異步執(zhí)行炉擅,都會(huì)創(chuàng)建新的線程辉懒,只不過(guò)調(diào)用堆棧,有些許不同
谍失,后面眶俩,我們講解代碼的時(shí)候,會(huì)根據(jù)堆棧來(lái)講解快鱼。
下面颠印,我來(lái)看一下,其他的情況:
dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue01, ^{
sleep(5);
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"111");
});
dispatch_async(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"222");
});
// 執(zhí)行結(jié)果
myDemo[39545:5914239] current thread = <NSThread: 0x60000027cb40>{number = 3, name = (null)}
myDemo[39545:5914239] 111
myDemo[39545:5914239] current thread = <NSThread: 0x60000027cb40>{number = 3, name = (null)}
myDemo[39545:5914239] 222
同一個(gè)串行隊(duì)列異步執(zhí)行抹竹,會(huì)在同一個(gè)異步線程中线罕,串行執(zhí)行
dispatch_queue_t queue01 = dispatch_queue_create("queue01", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue01, ^{
sleep(5);
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"111");
});
dispatch_async(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"222");
});
// 執(zhí)行結(jié)果
myDemo[39642:5919537] current thread = <NSThread: 0x600000265bc0>{number = 3, name = (null)}
myDemo[39642:5919537] 222
myDemo[39642:5919535] current thread = <NSThread: 0x60000007e700>{number = 4, name = (null)}
myDemo[39642:5919535] 111
同一個(gè)并行隊(duì)列,異步執(zhí)行窃判,會(huì)分別生成兩個(gè)不同的線程钞楼。相當(dāng)于是每一次的并行隊(duì)列,異步執(zhí)行袄琳,都會(huì)重啟一個(gè)線程執(zhí)行
// 全局隊(duì)列
dispatch_queue_t queue01 = dispatch_get_global_queue(0, 0);
dispatch_async(queue01, ^{
sleep(5);
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"111");
});
dispatch_async(queue01, ^{
NSLog(@"current thread = %@", [NSThread currentThread]);
NSLog(@"222");
});
// 執(zhí)行結(jié)果
myDemo[39753:5926161] current thread = <NSThread: 0x6040002656c0>{number = 3, name = (null)}
myDemo[39753:5926161] 222
myDemo[39753:5926162] current thread = <NSThread: 0x600000460fc0>{number = 4, name = (null)}
myDemo[39753:5926162] 111
可以上面的是同樣的結(jié)果询件。
異步源碼的分析
通過(guò)上面的串行隊(duì)列異步執(zhí)行的堆棧,我們清楚分別執(zhí)行:
_dispatch_queue_push
->start_wqthread
->_dispatch_call_block_and_release
#ifdef __BLOCKS__
void
dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
{
dispatch_continuation_t dc = _dispatch_continuation_alloc();
uintptr_t dc_flags = DISPATCH_OBJ_CONSUME_BIT;
_dispatch_continuation_init(dc, dq, work, 0, 0, dc_flags);
_dispatch_continuation_async(dq, dc);
}
持續(xù)更新唆樊。宛琅。。逗旁。
GitHub地址:https://github.com/BernardChina/BCStudy/blob/master/GCD%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.md