iOS GCD死鎖

「少俠,GCD的基本功,練的怎麼樣了?」bash

「通過一段時間的勤學苦練,我已經爐火純青了,處理問題又快又穩,小師妹都對我另眼相看了呢」async

「真是可喜可賀,預祝少俠早日贏得伊人芳心。今天老夫來再來祝你一臂之力,少走彎路,避開魔道。」spa

「魔道? 沒遇到啊」線程

「少俠請看」code

NSLog(@"畫方");
dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"畫圓");
});
NSLog(@"吹口哨");

複製代碼

「鵝.... 這個怎麼感受跟我平時使用的不同呢,dispatch_sync同步碰見主線程會發生什麼呢。小生愚鈍,還請大師指點」cdn

「要弄清楚這個問題,首先要搞清楚,這段代碼運行的在主隊列,串行隊列仍是並行隊列,不一樣隊列也會致使不一樣結果。」blog

在主隊列執行隊列

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"畫方");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"畫圓");
    });
    NSLog(@"吹口哨");
}

//輸出
畫方
複製代碼

上面的代碼輸出「畫方」,而後程序卡死在 dispatch_sync這一行。後面的任務再也執行不了。文檔

「大師,爲何會這樣呢」get

「先來複習一下,GCD的基本招式。」

dispatch_async(queue,block)   
dispatch_sync(queue,block)  

這兩個方法的功能是把 block中要執行的任務,放到指定的queue中。
dispatch_async(queue,block),會當即返回,不等block執行。  
dispatch_sync(queue,block),會阻塞當前線程,等待block執行完成。
複製代碼

代碼在viewDidLoad中執行,即在主線程中執行。畫方能夠順利執行,以後執行到dispatch_sync,這個代碼在主線程中執行,把block任務也放到主線程執行。主線程是串行隊列,前面一個任務執行完成以後,才能執行後面的任務,在這裏,dispatch_sync在前面,而被加入到主線程的block在後面,因此正常流程是dispatch_sync,而後執行block,但dispatch_sync必須等待block執行完成後,纔會執行完成,而根據串行隊列FIFO的規則,block必須等待dispatch_sync執行完成以後本身才能執行。這裏就形成了死鎖。代碼卡死在dispatch_sync這一行。

主隊列中執行

在並行隊列執行

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    NSLog(@"畫方");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"畫圓");
    });
    NSLog(@"吹口哨");
});

//輸出
畫方
畫圓
吹口哨
複製代碼

在並行隊列中時,畫方先執行,dispatch_sync會等待block中的任務畫圓在主隊列中完成以後,繼續執行吹口哨,由於這三個任務都在並行隊列中,因此不會存在死鎖,程序正常執行。

並行隊列中執行

「原來如此,多謝大師指點,讓我沒有誤入魔道。」

dispatch_sync 文檔中以下解釋:

Calls to dispatch_sync() targeting the current queue will   
result in dead-lock

假設當前代碼在queueA中執行,當用 dispatch_sync把block放到當前隊列queueA中時,就會產生死鎖。

下面這種狀況也會產生死鎖。

dispatch_queue_t queue = dispatch_queue_create("com.test.gcd", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    NSLog(@"畫方");
    dispatch_sync(queue, ^{
        NSLog(@"畫圓");
    });
    NSLog(@"吹口哨");
});

//輸出
畫方

程序卡死在dispatch_sync這一行。
複製代碼

「知道了箇中原理,遇到問題就能輕鬆化解了。將來的修煉之路已經給你指名,快快修煉去罷。」

相關文章
相關標籤/搜索