GCD 死鎖的原因(從同步線程的原理上講)



這里如果我將22222上面的更改為dispatch_sync(q, ^{ ,請問執(zhí)行結(jié)果是什么洲劣?



答案是執(zhí)行到這里崩潰了瓢颅,我們來分析一下,為什么這里會崩潰熙兔?很多博客上面都有說會崩潰

但是理由很牽強悲伶。大概是下面截圖這個意思。



老實說住涉,這個說法很牽強麸锉。看這段我自己都不能說服我自己舆声,下面是唯一能說服我的帖子花沉,是從源碼的角度來說的。
一個曲高和寡語言水平很捉急的大神給出的源碼
但是他的分析太難看懂了媳握,主要是因為語文水平很捉急碱屁。這里我?guī)痛蠹铱偨Y(jié)一下。
實際上GCD里面關(guān)于實現(xiàn)同步隊列使用到的是信號量毙芜,模擬出來大概是這樣子的
 @synchronized (self) {
               if (self.array.count synsc block里面需要被執(zhí)行的
                   //一個同步事件可以分為兩部分.  第一wait queue,  第二, 派發(fā)事件.
                   //此處為了方便,  兩行代碼位置是反的.  但是由于async 派遣, 本身就拼到了隊列末尾.  所以
                   //從實際執(zhí)行角度,   順序是沒有問題的.   完整模擬了.   dispatch源碼中對事件的執(zhí)行模式和 同步派遣到本身隊列的死鎖問題.
                   dispatch_async(queue, ^{
                       dispatch_semaphore_signal(semaphore);
                   });
                   dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

                   [self.array removeLastObject];
                   NSLog(@"\n");
                   NSLog(@"消費了  同步!!!!!!事件");
                   NSLog(@"\n");
                   syncEvent = NO;

               }else{
                   //普通消費.
                   dispatch_async(dispatch_get_global_queue(0, 0), ^{
                       @synchronized (self) {
                           [self.array removeLastObject];
                           NSLog(@"消費了一個異步事件.");
                       }
                   });
               }
           }

想要看懂上面這段代碼忽媒,你需要反復(fù)理解下面幾句話,我當(dāng)時想了一下午才想通腋粥。

串行隊列里面只可能有一個線程晦雨,并行隊列里面可能有多個架曹。
隊列里面可能沒有線程,線程總是跑來跑去的闹瞧。
不管是同步函數(shù)或者是異步函數(shù)绑雄,都會將block里面的內(nèi)容派遣到對應(yīng)的隊列的最下面。
同步函數(shù)里面維護了一套信號量奥邮,信號量的single操作被套在異步函數(shù)里面
dispatch_async(queue, ^{
dispatch_semaphore_signal(semaphore);
});
同步函數(shù)的的其他操作在同步函數(shù)所處的外面的隊列里面去執(zhí)行万牺,只有
dispatch_semaphore_signal(semaphore);
在同步函數(shù)鎖包裹的隊列里面去執(zhí)行。

綜上所述洽腺,這行代碼將信號量的++事件放到了queue隊列的最后脚粟。如果同步函數(shù)外面沒有對queue做派遣動作,不會死鎖蘸朋。

我們將這些理論放到實際例子里面去解釋

- (void)viewDidLoad {
   [super viewDidLoad];
   dispatch_sync(dispatch_get_main_queue(), ^{
       NSLog(@"1111");
   });

   NSLog(@"222");
}

外面是主隊列核无,主隊列里面是主線程,同步函數(shù)將NSLog(@"1111"); 壓倒主隊列的底部了藕坯。里外都是主隊列团南,事件順序執(zhí)行。

同步函數(shù)底層按照順序異步函數(shù)將dispatch_semaphore_signal(semaphore);

壓在queue(主隊列)的最下面

 dispatch_async(queue, ^{
        dispatch_semaphore_signal(semaphore);
 });

然后執(zhí)行

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

信號量--為-1炼彪,阻塞當(dāng)前線程
吐根,這樣程序員永遠(yuǎn)都執(zhí)行不到dispatch_semaphore_signal(semaphore)

思考

這個例子不知道大家看懂了沒有,是不是感覺這樣分析的話辐马,只要使用同步函數(shù)都會被死鎖拷橘。

其實同步函數(shù)的底層下面這個函數(shù)能夠執(zhí)行到線程就不會被鎖住

dispatch_async(queue, ^{
       dispatch_semaphore_signal(semaphore);
});

如何能執(zhí)行dispatch_semaphore_signal(semaphore);

信號量的++操作被壓在queue的最下面了,只要同步函數(shù)的執(zhí)行的queue和外面的queue不一致齐疙,這里就會被執(zhí)行了膜楷。

看這個例子,我們將同步函數(shù)派遣的主隊列換成一個新的串行隊列

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_sync(dispatch_queue_create("1111", DISPATCH_QUEUE_SERIAL), ^{
        NSLog(@"1111");
    });
 
    NSLog(@"222");
}

這樣同步函數(shù)的底層在主隊列里面執(zhí)行下面信號量--

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

主隊列里面執(zhí)行異步函數(shù)+串行隊列贞奋,將信號量++壓在串行隊列的底部,串行隊列唯一事件為信號量++函數(shù)穷绵,于是執(zhí)行解鎖

  dispatch_async(queue, ^{
        dispatch_semaphore_signal(semaphore);
 });

再來一個例子

dispatch_queue_t q = dispatch_queue_create("1111111", DISPATCH_QUEUE_SERIAL);
 
    dispatch_sync(q, ^{
 
        NSLog(@"11111");
        dispatch_sync(q, ^{
            NSLog(@"22222");
        });
        NSLog(@"33333");
    });
 
    NSLog(@"44444");
    NSLog(@"5555");

第一層同步函數(shù)處在主隊列里面轿塔,

同步函數(shù)的底層主隊列里面執(zhí)行

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

在新的串行隊列里面執(zhí)行

  dispatch_async(queue, ^{
        dispatch_semaphore_signal(semaphore);
 });

這兩步操作分開在不同的隊列里面,都能執(zhí)行仲墨,所以不會死鎖

第二層同步函數(shù)處在q隊列里面

同步函數(shù)的底層q串行隊列里面執(zhí)行勾缭,鎖住了q隊列,q隊列無法執(zhí)行其他事件

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

在q串行隊列里面執(zhí)行目养,被壓入隊列事件中俩由,但是因為信號量鎖住線程gg

dispatch_async(queue, ^{
        dispatch_semaphore_signal(semaphore);
 });
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市癌蚁,隨后出現(xiàn)的幾起案子幻梯,更是在濱河造成了極大的恐慌兜畸,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碘梢,死亡現(xiàn)場離奇詭異咬摇,居然都是意外死亡,警方通過查閱死者的電腦和手機煞躬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門肛鹏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人恩沛,你說我怎么就攤上這事在扰。” “怎么了雷客?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵健田,是天一觀的道長。 經(jīng)常有香客問我佛纫,道長妓局,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任呈宇,我火速辦了婚禮好爬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘甥啄。我一直安慰自己存炮,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布蜈漓。 她就那樣靜靜地躺著穆桂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪融虽。 梳的紋絲不亂的頭發(fā)上享完,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天,我揣著相機與錄音有额,去河邊找鬼般又。 笑死,一個胖子當(dāng)著我的面吹牛巍佑,可吹牛的內(nèi)容都是我干的茴迁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼萤衰,長吁一口氣:“原來是場噩夢啊……” “哼堕义!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起脆栋,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤倦卖,失蹤者是張志新(化名)和其女友劉穎洒擦,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糖耸,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡秘遏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了嘉竟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邦危。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖舍扰,靈堂內(nèi)的尸體忽然破棺而出倦蚪,到底是詐尸還是另有隱情,我是刑警寧澤边苹,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布陵且,位于F島的核電站,受9級特大地震影響个束,放射性物質(zhì)發(fā)生泄漏慕购。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一茬底、第九天 我趴在偏房一處隱蔽的房頂上張望沪悲。 院中可真熱鬧,春花似錦阱表、人聲如沸殿如。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涉馁。三九已至,卻和暖如春爱致,著一層夾襖步出監(jiān)牢的瞬間烤送,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工蒜鸡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留胯努,地道東北人。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓逢防,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蒲讯。 傳聞我的和親對象是個殘疾皇子忘朝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,654評論 2 354

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

  • GCD筆記 總結(jié)一下多線程部分,最強大的無疑是GCD,那么先從這一塊部分講起. Dispatch Queue的種類...
    jins_1990閱讀 761評論 0 1
  • 一、多線程簡介: 所謂多線程是指一個 進程 -- process(可以理解為系統(tǒng)中正在運行的一個應(yīng)用程序)中可以開...
    尋形覓影閱讀 1,030評論 0 6
  • “人是不可能做到“人人愛我”這件事的,因為一旦追求這個但指,就有了相反的東西產(chǎn)生——“我擔(dān)心別人不愛我””寡痰。 ...
    姜楊A(yù)da閱讀 178評論 0 0
  • 我記得自己18歲第一次談戀愛時,心里想的是棋凳,“一陣子”. 即使再無知拦坠,仿佛“初戀大多不會走得很遠(yuǎn)”的事實也是一個常...
    Skullman閱讀 446評論 0 0
  • 一座城,也許沒有那么多的故事可講 一座城剩岳,也許沒有那么多的繁花似錦 一座城贞滨,也許沒有那么多的人去關(guān)注 一座城,也許...
    唐曉柒閱讀 184評論 0 0