GCD有關問題:dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@"Hello ?");}); 死鎖的原因

dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@"Hello ?");});


這裡先分清兩個概念:Queue 和 Async、Sync。

Queue(隊列):隊列分為串列和並行。串列隊列上面你按照A、B、C、D的順序添加四個任務,這四個任務按順序執行,結束順序也肯定是A、B、C、D。而並行隊列上面這四個任務同時執行,完成的順序是隨機的,每次都可能不一樣。

Async VS Sync(非同步執行和同步執行):使用dispatch_async 調用一個block,這個block會被放到指定的queue隊尾等待執行,至於這個block是並行還是串列執行只和dispatch_async參數裡面指定的queue是並行和串列有關。但是dispatch_async會馬上返回。

使用dispatch_sync 同樣也是把block放到指定的queue上面執行,但是會等待這個block執行完畢才會返回,阻塞當前queue直到sync函數返回。

所以隊列是串列、並行 和 同步、非同步執行調用block是兩個完全不一樣的概念。

這兩個概念清楚了之後就知道為什麼死鎖了。

分兩種情況:

1、當前queue是串列隊列。當前queue上調用sync函數,並且sync函數中指定的queue也是當前queue。需要執行的block被放到當前queue的隊尾等待執行,因為這是一個串列的queue,

調用sync函數會阻塞當前隊列,等待block執行 -&> 這個block永遠沒有機會執行 -&> sync函數不返回,所以當前隊列就永遠被阻塞了,這就造成了死鎖。(這就是問題中在主線程調用sync函數,並且在sync函數中傳入main_queue作為queue造成死鎖的情況)。

2、當前queue是並行隊列。在並行的queue上面調用sync函數,同時傳入當前queue作為參數,並不會造成死鎖,因為block會馬上被執行,所以sync函數也不會一直等待不返回造成死鎖。但是在並行隊列上調用sync函數傳入當前隊列作為參數的用法,想不出什麼情況下才會這樣用。stackoverflow上面有一個針對這種情況的討論。

http://stackoverflow.com/a/19181474


在這裡只考慮Serial Dispatch Queue,比如Main Thread Queue;而Concurrent Dispatch Queue一般情況不會發生,暫時沒有研究。

dispatch_sync(queue1, block);假設該語句在queue0中執行,則會阻塞(等待)queue0直到queue1中的block執行結束。當queue0和queue1不同時,queue0與queue1為並行執行,此時queue0等待,queue1執行block,queue0與queue1互不影響,直到queue1的block執行結束,queue0阻塞也結束,即dispatch_sync語句返回。當queue0和queue1相同(且為串列隊列Serial Dispatch Queue)時,即dispatch_sync與block在同一個隊列中,此時queue0已經阻塞,不可能再執行block,更無法等待block執行結束,因此造成死鎖。

官方文檔指出:dispatch_sync的當前執行隊列與提交block執行的目標隊列相同時將造成死鎖。

Submits a block to a dispatch queue for synchronous execution. Unlike dispatch_async, this function does not return until the block has finished. Calling this function and targeting the current queue results in deadlock.


TL;DR

首先明白下面幾個 facts:

1. Dispatch 的核心是 Queue,分並行和串列兩種,Main Queue 是典型的串列 Queue。

2. dispatch_sync 方法要等待放入 Queue 的 block 完成才返回。

3. 放入 Main Queue 的 block 需要等當前執行的方法(通常是由一個 Source 0 事件觸發的)返回才能依次執行。

結論:相互等待條件成立,死鎖產生。


dispatch_sync runs a block on a given queue and waits for it to complete. In this case, the queue is the main dispatch queue. The main queue runs all its operations on the main thread, in FIFO (first-in-first-out) order. That means that whenever you call dispatch_sync, your new block will be put at the end of the line, and wont run until everything else before it in the queue is done.

The problem here is that the block you just enqueued is at the end of the line waiting to run on the main thread, but your testSample method is currently running on the main thread. The block at the end of the queue cant get access to the main thread until the current method finishes using the main thread


dispatch_sync在等待block語句執行完成,而block語句需要在主線程里執行,所以dispatch_sync如果在主線程調用就會造成死鎖


推薦閱讀:

iOS開發中遇到過的坑

TAG:iOS | iOS應用 | iOS開發 |