OC底層知識(shí)點(diǎn)之-多線程(三)GCD中篇

異步函數(shù)

先看下dispatch_async的底層實(shí)現(xiàn)

上圖我們發(fā)現(xiàn)有兩個(gè)主要方法:

  • 1._dispatch_continuation_init這個(gè)方法上篇最后講了用處:就是任務(wù)包裝渐夸,將work(任務(wù)執(zhí)行)綁定到dc的dc_ctxt中找颓,將方法綁定到dc的dc_func中
  • 2._dispatch_continuation_async是并發(fā)處理函數(shù),主要執(zhí)行block回調(diào)侨嘀。

_dispatch_continuation_init方法上篇講了,這里不再過(guò)多陳述。我們看下_dispatch_continuation_async方法侥钳。

_dispatch_continuation_async

我們進(jìn)入_dispatch_continuation_async的方法實(shí)現(xiàn)

方法中的關(guān)鍵代碼為2659行:dx_push(dqu._dq, dc, qos);(原因:這個(gè)方法的結(jié)果作為返回值返回涡相,前面只是對(duì)dqu進(jìn)行處理)哲泊。

dx_push

dx_push是個(gè)宏定義,如下圖所示:

我們看到dx_push最后會(huì)執(zhí)行dq_push方法催蝗,而dq_push方法進(jìn)行搜索時(shí)發(fā)現(xiàn)有很多切威。

通過(guò)上圖我們可以看到,.dq_push會(huì)根據(jù)隊(duì)列的不同類型丙号,執(zhí)行不同的函數(shù)先朦。我們注意下673-682行,662-671行犬缨,我們看到.do_type分別為DISPATCH_QUEUE_CONCURRENT_TYPE和DISPATCH_QUEUE_SERIAL_TYPE喳魏,類似并發(fā)隊(duì)列和串行隊(duì)列。我們通過(guò)發(fā)號(hào)斷點(diǎn)看看并發(fā)隊(duì)列是不是走_(dá)dispatch_lane_concurrent_push遍尺,串行隊(duì)列是不是走_(dá)dispatch_lane_push截酷。

先打斷點(diǎn):
    dispatch_queue_t conque = dispatch_queue_create("Lj", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(conque, ^{
        NSLog(@"12334");
    });

運(yùn)行代碼,在dispatch_async處打斷點(diǎn)乾戏,運(yùn)行到斷點(diǎn)處,在講符號(hào)斷點(diǎn)打開(kāi)三热,下一步鼓择,發(fā)現(xiàn)確實(shí)走到了_dispatch_lane_concurrent_push方法

下面我們看下串行隊(duì)列就漾。

    dispatch_queue_t serial = dispatch_queue_create("Lj", DISPATCH_QUEUE_SERIAL);
   dispatch_async(serial, ^{
       NSLog(@"12334");
   });

和驗(yàn)并行一樣呐能,發(fā)現(xiàn)確實(shí)執(zhí)行了_dispatch_lane_push方法

上面驗(yàn)證說(shuō)明了.dq_push確實(shí)會(huì)根據(jù)隊(duì)列的不同類型抑堡,執(zhí)行不同的函數(shù)摆出。

_dispatch_lane_concurrent_push

下面我們看下_dispatch_lane_concurrent_push方法,源碼實(shí)現(xiàn)

我們看到_dispatch_lane_concurrent_push源碼分兩步實(shí)現(xiàn):

  • 1._dispatch_continuation_redirect_push
  • 2._dispatch_lane_push

通過(guò)符號(hào)斷點(diǎn)運(yùn)行首妖,發(fā)現(xiàn)并發(fā)隊(duì)列會(huì)執(zhí)行_dispatch_continuation_redirect_push方法

源碼中搜索_dispatch_continuation_redirect_push

我們發(fā)現(xiàn)這個(gè)_dispatch_continuation_redirect_push也執(zhí)行的dx_push偎漫。上面我們分析_dispatch_continuation_async就會(huì)調(diào)用dx_push,此處也調(diào)用有缆,會(huì)形成遞歸嗎象踊?答案肯定是不會(huì)!原因在前面隊(duì)列創(chuàng)建時(shí)可知棚壁,隊(duì)列也是一個(gè)對(duì)象杯矩,有父類,根類袖外。所以此時(shí)調(diào)用dx_push是調(diào)用的父類或者根類方法史隆。

上面說(shuō)了dx_push會(huì)調(diào)用父類或者根類方法,上面說(shuō)了dx_push會(huì)調(diào)用dq_push曼验,上面我們羅列了dq_push會(huì)調(diào)用哪些方法泌射,根類方法在684-705行粘姜,此時(shí)調(diào)用的方法為_dispatch_root_queue_push,我們打斷點(diǎn)魄幕,看看是不是會(huì)走這個(gè)方法

通過(guò)上面圖我們知道并發(fā)隊(duì)列會(huì)調(diào)用_dispatch_root_queue_push方法相艇,所以我們上面說(shuō)的dx_push調(diào)用會(huì)調(diào)用父類或者根類是對(duì)的

我們來(lái)看下_dispatch_root_queue_push源碼纯陨,已將將無(wú)關(guān)緊要的隱藏了

我們通過(guò)_dispatch_root_queue_push看到執(zhí)行順序:_dispatch_root_queue_push->_dispatch_root_queue_push_inline->_dispatch_root_queue_poke->_dispatch_root_queue_poke_slow坛芽,我們主要看下_dispatch_root_queue_poke_slow的源碼

這個(gè)方法我們把不主要的方法給隱藏了,我們說(shuō)下主要方法:

  • 1.通過(guò)do-while循環(huán)創(chuàng)建線程翼抠,使用pthread_create方法創(chuàng)建(第6223行)
  • 2.通過(guò)_dispatch_root_queues_init方法注冊(cè)回調(diào)(第6233行)

下面我們看下_dispatch_root_queues_init源碼:

我們看到調(diào)用了dispatch_once_f咙轩,這個(gè)是個(gè)單例(后續(xù)會(huì)做個(gè)單例的底層分析,這里不做說(shuō)明)阴颖,其中傳入的func是_dispatch_root_queues_init_once活喊,下面我們?cè)俨榭聪耞dispatch_root_queues_init_once源碼實(shí)現(xiàn),我們看主要的量愧。

上圖我們看到進(jìn)入_dispatch_root_queues_init_once的源碼钾菊,其內(nèi)部不同事務(wù)的調(diào)用句柄是_dispatch_worker_thread2(第7641行,第7650行偎肃,7666行)煞烫。

通過(guò)上面可以知道,其block回調(diào)執(zhí)行的調(diào)用路徑為:_dispatch_root_queues_init_once -> _dispatch_worker_thread2 -> _dispatch_root_queue_drain -> _dispatch_continuation_pop_inline -> _dispatch_continuation_invoke_inline -> _dispatch_client_callout -> dispatch_call_block_and_release 下面我們通過(guò)打印堆棧信息來(lái)驗(yàn)證一下

通過(guò)打印得出我們的推測(cè)是對(duì)的累颂。

說(shuō)明

這里需要說(shuō)明一點(diǎn)的是滞详,單例的block回調(diào)異步函數(shù)的block回調(diào)是不同的

  • 1.單例中block回調(diào)中的func是_dispatch_Block_invoke(block)
  • 2.而異步函數(shù)中紊馏,block回調(diào)中的func是dispatch_call_block_and_release

總結(jié)

綜上所述料饥,異步函數(shù)的底層分析如下

  • 1.【準(zhǔn)備工作】:首先,將異步任務(wù)拷貝并封裝朱监,并設(shè)置回調(diào)函數(shù)func
  • 2.【block回調(diào)】:底層通過(guò)dx_push遞歸岸啡,會(huì)重定向到根隊(duì)列,然后通過(guò)pthread_creat創(chuàng)建線程赌朋,最后通過(guò)dx_invoke執(zhí)行block回調(diào)(注意dx_push 和 dx_invoke 是成對(duì)的)

同步函數(shù)

進(jìn)入dispatch_async源碼實(shí)現(xiàn)凰狞,其底層實(shí)現(xiàn)是通過(guò)柵欄函數(shù)實(shí)現(xiàn)的(柵欄函數(shù)后面說(shuō)明)

上面我們可以看到dispatch_sync->_dispatch_sync_f->_dispatch_sync_f_inline順序執(zhí)行,看下_dispatch_sync_f_inline的方法

  • 1.dq->dq_width等于1表示串行隊(duì)列
  • 2._dispatch_barrier_sync_f為柵欄函數(shù)沛慢,可以看到同步函數(shù)的底層實(shí)現(xiàn)其實(shí)是同步柵欄函數(shù)赡若。
  • 3._dispatch_sync_f_slow死鎖,我們之前驗(yàn)證相互等待的死鎖報(bào)錯(cuò)時(shí)团甲,就執(zhí)行了這個(gè)方法逾冬。

我們看下_dispatch_sync_f_slow的具體方法實(shí)現(xiàn),調(diào)用_dispatch_sync_f_slow方法,表明當(dāng)前的隊(duì)列是掛起身腻,阻塞的

往一個(gè)隊(duì)列加入任務(wù)产还,會(huì)push加入主隊(duì)列,進(jìn)入_dispatch_trace_item_push

dispatch_trace_item_push方法執(zhí)行完成后嘀趟,就會(huì)執(zhí)行__DISPATCH_WAIT_FOR_QUEUE_脐区。下面我們看下DISPATCH_WAIT_FOR_QUEUE

_dispatch_wait_prepare:判斷dq是否為正在等待的主隊(duì)列,然后給出一個(gè)狀態(tài)state她按,然后將dq的狀態(tài)和當(dāng)前任務(wù)依賴的隊(duì)列進(jìn)行匹配

接著執(zhí)行_dq_state_drain_locked_by->_dispatch_lock_is_locked_by

如果當(dāng)前等待和正在執(zhí)行的是同一個(gè)隊(duì)列(判斷線程的id是否相等)牛隅,如果是同一個(gè)隊(duì)列即判斷為死鎖

同步函數(shù)+并發(fā)隊(duì)列

在_dispatch_sync_invoke_and_complete -> _dispatch_sync_function_invoke_inline源碼中,主要有三個(gè)步驟:

  • 1.將任務(wù)壓入隊(duì)列:_dispatch_thread_frame_push
  • 2.執(zhí)行任務(wù)的block回調(diào):_dispatch_client_callout
  • 3.將任務(wù)出隊(duì):_dispatch_thread_frame_pop

從實(shí)現(xiàn)中可以看出酌泰,是先將任務(wù)push隊(duì)列中媒佣,然后執(zhí)行block回調(diào),再將任務(wù)pop陵刹,所以任務(wù)是順序執(zhí)行的默伍。

總結(jié)

同步函數(shù)的底層實(shí)現(xiàn):

  • 1.同步函數(shù)的底層實(shí)現(xiàn)實(shí)際是同步柵欄函數(shù)
  • 2.同步函數(shù)中如果當(dāng)前正在執(zhí)行的隊(duì)列和等待的是同一個(gè)隊(duì)列形成相互等待的局面衰琐,則會(huì)造成死鎖

寫到最后

這篇文章主要講了dispatch_async(異步任務(wù))和dispatch_sync(同步任務(wù))底層方法調(diào)用過(guò)程也糊,也解釋也死鎖的底層判斷。下篇我們主要研究下柵欄函數(shù)羡宙,單例显设,信號(hào)量。

收錄

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辛辨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子瑟枫,更是在濱河造成了極大的恐慌斗搞,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慷妙,死亡現(xiàn)場(chǎng)離奇詭異僻焚,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)膝擂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門虑啤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人架馋,你說(shuō)我怎么就攤上這事狞山。” “怎么了叉寂?”我有些...
    開(kāi)封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵萍启,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)勘纯,這世上最難降的妖魔是什么局服? 我笑而不...
    開(kāi)封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮驳遵,結(jié)果婚禮上淫奔,老公的妹妹穿的比我還像新娘。我一直安慰自己堤结,他們只是感情好唆迁,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著霍殴,像睡著了一般媒惕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上来庭,一...
    開(kāi)封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天妒蔚,我揣著相機(jī)與錄音,去河邊找鬼月弛。 笑死肴盏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的帽衙。 我是一名探鬼主播菜皂,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼厉萝!你這毒婦竟也來(lái)了恍飘?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤谴垫,失蹤者是張志新(化名)和其女友劉穎章母,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體翩剪,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乳怎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了前弯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佑吝。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纳猫,死狀恐怖煎殷,靈堂內(nèi)的尸體忽然破棺而出嵌施,到底是詐尸還是另有隱情,我是刑警寧澤剃根,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布哩盲,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏廉油。R本人自食惡果不足惜惠险,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望抒线。 院中可真熱鬧班巩,春花似錦、人聲如沸嘶炭。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)眨猎。三九已至抑进,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間睡陪,已是汗流浹背寺渗。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留兰迫,地道東北人信殊。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像汁果,于是被迫代替她去往敵國(guó)和親涡拘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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