在這里,我創建了具有 .background 優先級的并發佇列:
let background = DispatchQueue(label: "backgroundQueue",
qos: .background,
attributes: [],
autoreleaseFrequency: .inherit,
target: nil)
當我嘗試DispatchQueue.main.sync異步呼叫此佇列時,它會成功執行
background.async {
DispatchQueue.main.sync {
print("Hello from background async")
}
}
但是,如果我嘗試DispatchQueue.main.sync從這個佇列同步呼叫它會導致死鎖
background.sync {
DispatchQueue.main.sync {
print("Hello from background sync")
}
}
為什么DispatchQueue.main.sync從并發佇列異步呼叫成功但同步失敗?
uj5u.com熱心網友回復:
參考蘋果檔案
。同步
該函式將塊提交到指定的調度佇列以同步執行。與 dispatch_async( : :)不同,此函式在塊完成之前不會回傳
這意味著當你第一次呼叫background.sync {control 是在屬于主佇列(這是一個序列化佇列)的主執行緒上時,一旦陳述句background.sync {被執行,控制停止在主佇列,現在等待塊完成執行
但是在內部,background.sync {您通過參考DispatchQueue.main.sync {并提交另一個同步執行塊來再次訪問主佇列,該塊只列印“來自后臺同步的Hello”,但是控制元件已經在等待主佇列回傳,background.sync {因此您最終創建了一個死鎖。
主佇列正在等待控制從后臺佇列回傳,后臺佇列又在等待主佇列完成列印陳述句的執行:|
事實上,蘋果在其描述中特別提到了這個用例
呼叫此函式并以當前佇列為目標會導致死鎖。
附加資訊:
通過訪問后臺佇列中的主佇列,您只需間接建立回圈依賴,如果您真的想測驗上述陳述句,您可以簡單地這樣做
let background = DispatchQueue(label: "backgroundQueue",
qos: .background,
attributes: [],
autoreleaseFrequency: .inherit,
target: nil)
background.sync {
background.sync {
print("Hello from background sync")
}
}
很明顯,你指的是background佇列里面background.sync會導致死鎖,這是蘋果檔案在其描述中指定的。您的情況在某種意義上略有不同,您提到了間接導致死鎖的主佇列
async在這些陳述句中的任何一個中使用如何打破delock?
現在你可以使用asyncinbackground.async {或 inDispatchQueue.main.async并且死鎖基本上會打破(我不是在這里建議哪個是正確的,哪個是正確的取決于你的需要和你想完成什么,但要打破僵局,你可以async在任何一個中使用那些派遣宣告,你會沒事的)
我將只解釋為什么死鎖只會在一種情況下打破(您顯然可以推斷出其他情況的解決方案)。讓我們說你使用
background.sync {
DispatchQueue.main.async {
print("Hello from background sync")
}
}
現在主佇列正在等待塊完成執行,您將其提交給后臺佇列以使用同步執行,background.sync并在內部background.sync再次使用訪問主佇列,DispatchQueue.main但這次您提交塊以進行異步執行。因此控制不會等待塊完成執行而是立即回傳。因為你提交到后臺佇列的block中沒有其他陳述句,它標志著任務完成,因此控制回傳到主佇列。現在主佇列會處理提交的任務,每當它處理你的print("Hello from background sync")塊時,它就會列印它。
uj5u.com熱心網友回復:
.sync意味著它將阻塞當前作業的執行緒,并等待閉包執行完畢。所以你的第一個.sync將阻塞主執行緒(你必須在主執行緒中執行 .sync 否則它不會死鎖)。并等到關閉background.sync {...}完成,然后才能繼續。
但是第二個閉包阻塞了后臺執行緒,并為已經阻塞的主執行緒分配了一個新作業。所以這兩個執行緒永遠在等待對方。
但是如果你切換你的啟動背景關系,比如在后臺執行緒中啟動你的代碼,可以解決死鎖。
// define another background thread
let background2 = DispatchQueue(label: "backgroundQueue2",
qos: .background,
attributes: [],
autoreleaseFrequency: .inherit,
target: nil)
// don't start sample code in main thread.
background2.async {
background.sync {
DispatchQueue.main.sync {
print("Hello from background sync")
}
}
}
這些死鎖是由.sync串行佇列中的操作引起的。只需呼叫DispatchQueue.main.sync {...}就會重現該問題。
// only use this could also cause the deadlock.
DispatchQueue.main.sync {
print("Hello from background sync")
}
或者在一開始就不要阻塞主執行緒也可以解決死鎖。
background.async {
DispatchQueue.main.sync {
print("Hello from background sync")
}
}
結論
.sync串行佇列中的操作可能會導致永久等待,因為它是單執行緒的。它不能立即停止并期待一份新作業。它目前正在做的作業應該先完成,然后才能開始另一個。這就是.sync不能在串行佇列中使用的原因。
uj5u.com熱心網友回復:
DispatchQueue 有兩種型別:
- 串行佇列 - 一旦前一個完成執行,一個作業項開始執行
- 并發佇列 - 作業項并發執行
它還有兩種調度技術:
- 同步 - 它阻塞呼叫執行緒,直到執行未完成(您的代碼等待該專案完成執行)
- 異步 - 它不會阻塞呼叫執行緒,并且您的代碼在作業項在其他地方運行時繼續執行
注意: 嘗試同步執行主佇列上的作業項會導致死鎖。
對于 Apple 檔案:
截取自DispatchQueue 檔案的螢屏截圖,其中包括:
重要的
嘗試同步執行主佇列上的作業項會導致死鎖。
結論:永遠不要在主佇列上調度同步。你遲早會陷入僵局。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/318762.html
