說說GCD中的死鎖

本文主要舉例說明GCD里的死鎖場(chǎng)景,分析造成死鎖的原因以及解決方案

在開始說GCD死鎖之前沪猴,我們先了解一下GCD的中的任務(wù)派發(fā)和隊(duì)列璃吧。

任務(wù)派發(fā)
任務(wù)派發(fā)方式 說明
dispatch_sync() 同步執(zhí)行嫡纠,完成了它預(yù)定的任務(wù)后才返回递沪,阻塞當(dāng)前線程
dispatch_async() 異步執(zhí)行,會(huì)立即返回沫勿,預(yù)定的任務(wù)會(huì)完成但不會(huì)等它完成挨约,不阻塞當(dāng)前線程
隊(duì)列種類
隊(duì)列種類 說明
串行隊(duì)列 每次只能執(zhí)行一個(gè)任務(wù),并且必須等待前一個(gè)執(zhí)行任務(wù)完成
并發(fā)隊(duì)列 一次可以并發(fā)執(zhí)行多個(gè)任務(wù)产雹,不必等待執(zhí)行中的任務(wù)完成
GCD隊(duì)列種類
GCD隊(duì)列種類 獲取方法 隊(duì)列類型 說明
主隊(duì)列 dispatch_get_main_queue 串行隊(duì)列 主線中執(zhí)行
全局隊(duì)列 dispatch_get_global_queue 并發(fā)隊(duì)列 子線程中執(zhí)行
用戶隊(duì)列 dispatch_queue_create 串并都可以 子線程中執(zhí)行

GCD死鎖

在GCD中诫惭,主要的死鎖就是當(dāng)前串行隊(duì)列里面同步執(zhí)行當(dāng)前串行隊(duì)列。解決的方法就是將同步的串行隊(duì)列放到另外一個(gè)線程執(zhí)行蔓挖。

死鎖場(chǎng)景

1. 死鎖場(chǎng)景: 主線程調(diào)用主線程
- (void)deadLockCase1 {
    NSLog(@"1"); // 任務(wù)1
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2"); // 任務(wù)2
    });
    NSLog(@"3"); // 任務(wù)3
}

控制臺(tái)輸出:

1
原因:

從控制臺(tái)輸出可以看出夕土,任務(wù)2和任務(wù)3沒有執(zhí)行,此時(shí)已經(jīng)死鎖了瘟判。
因?yàn)閐ispatch_sync是同步的怨绣,本身就會(huì)阻塞當(dāng)前線程,此刻阻塞了主線程拷获。而當(dāng)前block又在等待主線程執(zhí)行完畢篮撑,從而形成了主線程等待主線程,自己等自己的情況匆瓜,形成了死鎖赢笨。

解決方法:
  1. 改用異步dispatch_async執(zhí)行
NSLog(@"1"); // 任務(wù)1
dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"2"); // 任務(wù)2
 });
NSLog(@"3"); // 任務(wù)3

控制臺(tái)輸出:

1
3
2 
  1. 不在主線程中運(yùn)行,而是放在子線程中
NSLog(@"1"); // 任務(wù)1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSLog(@"2"); // 任務(wù)2
});
NSLog(@"3"); // 任務(wù)3

控制臺(tái)輸出:

1
2
3

注:如果block中是刷新UI的操作驮吱,則不能放在子線程中執(zhí)行茧妒,會(huì)crash

死鎖場(chǎng)景2: (同步串行隊(duì)列嵌套自己)
- (void)deadLockCase2 {
    dispatch_queue_t aSerialDispatchQueue = dispatch_queue_create("com.test.deadlock.queue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1"); //任務(wù)1
    dispatch_sync(aSerialDispatchQueue, ^{
        NSLog(@"2"); //任務(wù)2
        dispatch_sync(aSerialDispatchQueue, ^{
            NSLog(@"3"); //任務(wù)3
        });
        NSLog(@"4");  //任務(wù)4
    });
    NSLog(@"5");  //任務(wù)5
}

控制臺(tái)輸出:

1
2
原因:

從控制臺(tái)輸出結(jié)果來看,執(zhí)行到任務(wù)2后糠馆,就已經(jīng)死鎖了嘶伟。因?yàn)樵摾又袃蓚€(gè)GCD都是使用的同步方式,而且還是同一個(gè)串行隊(duì)列又碌,這就導(dǎo)致了和上一個(gè)例子一樣九昧,自己在等待自己的情況,形成了死鎖毕匀。

解決方法:
  1. 將第二個(gè)GCD改為異步
 - (void)deadLockCase2 {
    dispatch_queue_t aSerialDispatchQueue = dispatch_queue_create("com.test.deadlock.queue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1"); //任務(wù)1
    dispatch_sync(aSerialDispatchQueue, ^{
        NSLog(@"2"); //任務(wù)2
        dispatch_async(aSerialDispatchQueue, ^{
            NSLog(@"3"); //任務(wù)3
        });
        NSLog(@"4");  //任務(wù)4
    });
    NSLog(@"5");  //任務(wù)5
}

控制臺(tái)輸出:

1
2
4
5
3

然而铸鹰,將第一個(gè)GCD改為異步,不能解決問題

 - (void)deadLockCase2 {
    dispatch_queue_t aSerialDispatchQueue = dispatch_queue_create("com.test.deadlock.queue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1"); //任務(wù)1
    dispatch_async(aSerialDispatchQueue, ^{
        NSLog(@"2"); //任務(wù)2
        dispatch_sync(aSerialDispatchQueue, ^{
            NSLog(@"3"); //任務(wù)3
        });
        NSLog(@"4");  //任務(wù)4
    });
    NSLog(@"5");  //任務(wù)5
}

控制臺(tái)輸出:

1
5
2
原因:

雖然第一個(gè)GCD是異步的皂岔,但是第二個(gè)GCD是同步的蹋笼,第二個(gè)GCD在等著第一個(gè)GCD結(jié)束,而第一個(gè)GCD的block又在等著第一個(gè)GCD結(jié)束躁垛,這樣就形成了死鎖剖毯。
注:對(duì)于以上將第二個(gè)GCD改為異步,第一個(gè)GCD為同步的場(chǎng)景教馆,不會(huì)造成死鎖逊谋,是因?yàn)榈诙€(gè)GCD為異步,它不用等待第一個(gè)GCD執(zhí)行完畢土铺,它和第一個(gè)GCD是沒有同步關(guān)系的胶滋。它是在第一個(gè)GCD執(zhí)行的同時(shí)并發(fā)執(zhí)行自己block的代碼。

  1. 將兩個(gè)GCD都改為異步
 - (void)deadLockCase2 {
    dispatch_queue_t aSerialDispatchQueue = dispatch_queue_create("com.test.deadlock.queue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1"); //任務(wù)1
    dispatch_async(aSerialDispatchQueue, ^{
        NSLog(@"2"); //任務(wù)2
        dispatch_async(aSerialDispatchQueue, ^{
            NSLog(@"3"); //任務(wù)3
        });
        NSLog(@"4");  //任務(wù)4
    });
    NSLog(@"5");  //任務(wù)5
}

控制臺(tái)輸出:

1
5
2
4
3
  1. 使用不同的串行隊(duì)列
 - (void)deadLockCase2 {
    dispatch_queue_t aSerialDispatchQueue1 = dispatch_queue_create("com.test.deadlock.queue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t aSerialDispatchQueue2 = dispatch_queue_create("com.test.deadlock.queue2", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1"); //任務(wù)1
    dispatch_sync(aSerialDispatchQueue1, ^{
        NSLog(@"2"); //任務(wù)2
        dispatch_sync(aSerialDispatchQueue2, ^{
            NSLog(@"3"); //任務(wù)3
        });
        NSLog(@"4");  //任務(wù)4
    });
    NSLog(@"5");  //任務(wù)5
}

控制臺(tái)輸出:

1
2
3
4
5
3. 死鎖場(chǎng)景: 信號(hào)量阻塞主線程
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    NSLog(@"semaphore create!");
    dispatch_async(dispatch_get_main_queue(), ^{
        dispatch_semaphore_signal(semaphore);
        NSLog(@"semaphore plus 1");
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"semaphore minus 1");
}
原因:

如果當(dāng)前執(zhí)行的線程是主線程悲敷,以上代碼就會(huì)出現(xiàn)死鎖究恤。
因?yàn)?code>dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)阻塞了當(dāng)前線程,而且等待時(shí)間是DISPATCH_TIME_FOREVER——永遠(yuǎn)等待后德,這樣它就永遠(yuǎn)的阻塞了當(dāng)前線程——主線程部宿。導(dǎo)致主線中的dispatch_semaphore_signal(semaphore)沒有執(zhí)行,
dispatch_semaphore_wait一直在等待dispatch_semaphore_signal改變信號(hào)量瓢湃,這樣就形成了死鎖窟赏。

解決方法:

應(yīng)該將信號(hào)量移到并行隊(duì)列中,如全局調(diào)度隊(duì)列箱季。以下場(chǎng)景涯穷,移到串行隊(duì)列也是可以的。但是串行隊(duì)列還是有可能死鎖的(如果執(zhí)行dispatch_semaphore_signal方法還是在對(duì)應(yīng)串行隊(duì)列中的話藏雏,即之前提到的串行隊(duì)列嵌套串行隊(duì)列的場(chǎng)景)拷况。

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        NSLog(@"semaphore create!");
        dispatch_async(dispatch_get_main_queue(), ^{
            dispatch_semaphore_signal(semaphore);
            NSLog(@"semaphore plus 1");
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"semaphore minus 1");
    });
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市掘殴,隨后出現(xiàn)的幾起案子赚瘦,更是在濱河造成了極大的恐慌,老刑警劉巖奏寨,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件起意,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡病瞳,警方通過查閱死者的電腦和手機(jī)揽咕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門悲酷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人亲善,你說我怎么就攤上這事设易。” “怎么了蛹头?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵顿肺,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我渣蜗,道長(zhǎng)屠尊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任耕拷,我火速辦了婚禮讼昆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘斑胜。我一直安慰自己控淡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布止潘。 她就那樣靜靜地躺著掺炭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凭戴。 梳的紋絲不亂的頭發(fā)上涧狮,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音么夫,去河邊找鬼者冤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛档痪,可吹牛的內(nèi)容都是我干的涉枫。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼腐螟,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼愿汰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起乐纸,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤衬廷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后汽绢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吗跋,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了跌宛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酗宋。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖秩冈,靈堂內(nèi)的尸體忽然破棺而出本缠,到底是詐尸還是另有隱情斥扛,我是刑警寧澤入问,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站稀颁,受9級(jí)特大地震影響芬失,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜匾灶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一棱烂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧阶女,春花似錦颊糜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至憔杨,卻和暖如春鸟赫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背消别。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工抛蚤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寻狂。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓岁经,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蛇券。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缀壤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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