iOS-GCD源碼解析(二)dispatch_sync的實(shí)現(xiàn)

前面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)量

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市思灌,隨后出現(xiàn)的幾起案子俺叭,更是在濱河造成了極大的恐慌,老刑警劉巖泰偿,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绪颖,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡甜奄,警方通過查閱死者的電腦和手機(jī)柠横,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來课兄,“玉大人牍氛,你說我怎么就攤上這事⊙滩” “怎么了搬俊?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蜒茄。 經(jīng)常有香客問我唉擂,道長(zhǎng),這世上最難降的妖魔是什么檀葛? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任玩祟,我火速辦了婚禮,結(jié)果婚禮上屿聋,老公的妹妹穿的比我還像新娘空扎。我一直安慰自己藏鹊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布转锈。 她就那樣靜靜地躺著盘寡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪撮慨。 梳的紋絲不亂的頭發(fā)上竿痰,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音砌溺,去河邊找鬼菇曲。 笑死,一個(gè)胖子當(dāng)著我的面吹牛抚吠,可吹牛的內(nèi)容都是我干的常潮。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼楷力,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼喊式!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起萧朝,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤岔留,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后检柬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體献联,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年何址,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了里逆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡用爪,死狀恐怖原押,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情偎血,我是刑警寧澤诸衔,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站颇玷,受9級(jí)特大地震影響笨农,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜帖渠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一谒亦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦诊霹、人聲如沸羞延。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至入愧,卻和暖如春鄙漏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背棺蛛。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工怔蚌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人旁赊。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓桦踊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親终畅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子籍胯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容