为什么两个不同的串行队列在 swift 中产生死锁?
Why two different serial queue creating deadlock in swift?
我有一个自定义串行队列,在这个队列中同步调用主队列。它正在制造僵局。根据我的理解,两者都是独立的队列,因此它应该可以工作,并且(第 3 步和第 5 步)同步块都应该执行。谁能解释为什么会产生死锁?下面是我的游乐场代码。
func serialQueueTest() {
let customSerialQueue = DispatchQueue(label: "com.test.dipak")
print("Step 1")
customSerialQueue.async {
print("Step 2")
DispatchQueue.main.sync {
print("Step 3: Inside main sync queue")
}
}
print("Step 4")
customSerialQueue.sync {
print("Step 5: Inside Custom Serial Queue sync queue")
}
}
您正在阻止 main。
在您从 main 调用的第 4 步,main 将块提交到队列并等待它完成。但是那时你已经提交了第一个块(第 1 步),它正在等待 main 释放。
编辑
请注意,CSQ 并没有阻塞尝试执行您提交的两个块,而是 CSQ 和 main 正在阻塞等待彼此完成。我可以很容易地说明这一点 if 队列上有一个 isBusy
函数,但既然没有,让我们假装有,看看下面的代码。
func serialQueueTest() {
let customSerialQueue = DispatchQueue(label: "com.test.dipak")
print("Step 1")
customSerialQueue.async {
print("Step 2")
// Previously
// DispatchQueue.main.sync { AAA }
// same as below pseudo code
while ( main.isBusy )
{
wait ... *without* releasing control
}
now, on main, do AAA and then proceed
print ( "****" )
print ( "CSQ will now wait for main to finish what it is doing ..." )
print ( "But note, it does not release control or do something else," )
print ( "it *blocks* until main is finished. So it deadlocks." )
}
print("Step 4")
// Previously
// customSerialQueue.sync BBB
// replaced with ...
while ( csq.isBusy )
{
wait ... *without* releasing control
}
now, on csq, do BBB then proceed
print ( "****" )
print ( "Main will now wait for csq to finish what it is doing ..." )
print ( "But note, it does not release control or do something else," )
print ( "it *blocks* until csq is finished. So it deadlocks." )
}
即使我只向 CSQ 提交了 一个 块,这也会被块。
要打破僵局,您可以例如当您等待释放控制时(在这种情况下,您可以调用异步而不是同步)或使用不同类型的锁或以不同的方式等待另一个完成。
编辑 2
让我把它归结为本质。
// This runs on main
// This runs on csq
csq.async { main.sync // csq now waits on main to free up }
csq.sync // main now waits on csq to free up
// and you have deadlock
为了扩展 Tushar Sharma 和 Dipak 的回答,我将按照代码的执行顺序逐步浏览代码。
// == Scheduling a work item from the main queue ==
// Create a new serial queue
let customSerialQueue = DispatchQueue(label: "com.test.dipak")
// Create a work item and append it to customSerialQueue.
// Don't think of this being "in parallel." These are not threads. They're
// queues. It will run the next time customSerialQueue is scheduled. That might
// be immediately (if there's an available core), and that might be in the
// arbitrarily distant future. It doesn't matter what's in this work item. It's
// just "some work to do."
customSerialQueue.async { ... }
// On main queue still
print("Step 4")
// Create a work item, append it to customSerialQueue, and wait for it to
// complete. As before, it doesn't matter what's in this work item. It's just
// stuck onto the end of customSerialQueue and will execute when it gets to the
// front of the queue and the queue is scheduled. Currently it's 2nd in line
// after the Step 2 work item.
customSerialQueue.sync { ... }
此时,main 必须 yield (block)。它无法进步并完成
当前工作项(运行ning serialQueueTest)直到第 5 步工作项完成。
由于现在什么都没有 运行ning,所以 customSerialQueue 上的第一个块可以 运行。
// == Scheduling a work item from customSerialQueue ==
print("Step 2")
// Create a block, append it the main queue, and wait for it to complete.
DispatchQueue.main.sync { ... }
和以前一样,customSerialQueue 必须屈服(阻塞)。在第 3 步工作项完成之前,它无法继续并完成当前工作项(运行ning 第 2 步)。在 main 完成当前 运行ning 的工作项之前,无法安排第 3 步工作项(main 是一个串行队列)。
此时,main 被阻塞等待“第 5 步”块完成,customSerialQueue 被阻塞等待“第 3 步”完成。这是典型的死锁,任务都无法进行。
None 多核存在下的上述变化。 GCD 队列是关于并发性的,而不是并行性(和 concurrency is not parallelism)。它们与运行“同时”发生的事情无关。它们是关于安排工作项目的。因此,您应该首先对它们进行推理,就好像它们 运行 在单个内核上运行一样。然后,您可以添加有关如果同时有两个工作项 运行 会发生什么的问题。但是这个问题并没有改变依赖关系的基本问题。
我有一个自定义串行队列,在这个队列中同步调用主队列。它正在制造僵局。根据我的理解,两者都是独立的队列,因此它应该可以工作,并且(第 3 步和第 5 步)同步块都应该执行。谁能解释为什么会产生死锁?下面是我的游乐场代码。
func serialQueueTest() {
let customSerialQueue = DispatchQueue(label: "com.test.dipak")
print("Step 1")
customSerialQueue.async {
print("Step 2")
DispatchQueue.main.sync {
print("Step 3: Inside main sync queue")
}
}
print("Step 4")
customSerialQueue.sync {
print("Step 5: Inside Custom Serial Queue sync queue")
}
}
您正在阻止 main。
在您从 main 调用的第 4 步,main 将块提交到队列并等待它完成。但是那时你已经提交了第一个块(第 1 步),它正在等待 main 释放。
编辑
请注意,CSQ 并没有阻塞尝试执行您提交的两个块,而是 CSQ 和 main 正在阻塞等待彼此完成。我可以很容易地说明这一点 if 队列上有一个 isBusy
函数,但既然没有,让我们假装有,看看下面的代码。
func serialQueueTest() {
let customSerialQueue = DispatchQueue(label: "com.test.dipak")
print("Step 1")
customSerialQueue.async {
print("Step 2")
// Previously
// DispatchQueue.main.sync { AAA }
// same as below pseudo code
while ( main.isBusy )
{
wait ... *without* releasing control
}
now, on main, do AAA and then proceed
print ( "****" )
print ( "CSQ will now wait for main to finish what it is doing ..." )
print ( "But note, it does not release control or do something else," )
print ( "it *blocks* until main is finished. So it deadlocks." )
}
print("Step 4")
// Previously
// customSerialQueue.sync BBB
// replaced with ...
while ( csq.isBusy )
{
wait ... *without* releasing control
}
now, on csq, do BBB then proceed
print ( "****" )
print ( "Main will now wait for csq to finish what it is doing ..." )
print ( "But note, it does not release control or do something else," )
print ( "it *blocks* until csq is finished. So it deadlocks." )
}
即使我只向 CSQ 提交了 一个 块,这也会被块。
要打破僵局,您可以例如当您等待释放控制时(在这种情况下,您可以调用异步而不是同步)或使用不同类型的锁或以不同的方式等待另一个完成。
编辑 2
让我把它归结为本质。
// This runs on main
// This runs on csq
csq.async { main.sync // csq now waits on main to free up }
csq.sync // main now waits on csq to free up
// and you have deadlock
为了扩展 Tushar Sharma 和 Dipak 的回答,我将按照代码的执行顺序逐步浏览代码。
// == Scheduling a work item from the main queue ==
// Create a new serial queue
let customSerialQueue = DispatchQueue(label: "com.test.dipak")
// Create a work item and append it to customSerialQueue.
// Don't think of this being "in parallel." These are not threads. They're
// queues. It will run the next time customSerialQueue is scheduled. That might
// be immediately (if there's an available core), and that might be in the
// arbitrarily distant future. It doesn't matter what's in this work item. It's
// just "some work to do."
customSerialQueue.async { ... }
// On main queue still
print("Step 4")
// Create a work item, append it to customSerialQueue, and wait for it to
// complete. As before, it doesn't matter what's in this work item. It's just
// stuck onto the end of customSerialQueue and will execute when it gets to the
// front of the queue and the queue is scheduled. Currently it's 2nd in line
// after the Step 2 work item.
customSerialQueue.sync { ... }
此时,main 必须 yield (block)。它无法进步并完成 当前工作项(运行ning serialQueueTest)直到第 5 步工作项完成。
由于现在什么都没有 运行ning,所以 customSerialQueue 上的第一个块可以 运行。
// == Scheduling a work item from customSerialQueue ==
print("Step 2")
// Create a block, append it the main queue, and wait for it to complete.
DispatchQueue.main.sync { ... }
和以前一样,customSerialQueue 必须屈服(阻塞)。在第 3 步工作项完成之前,它无法继续并完成当前工作项(运行ning 第 2 步)。在 main 完成当前 运行ning 的工作项之前,无法安排第 3 步工作项(main 是一个串行队列)。
此时,main 被阻塞等待“第 5 步”块完成,customSerialQueue 被阻塞等待“第 3 步”完成。这是典型的死锁,任务都无法进行。
None 多核存在下的上述变化。 GCD 队列是关于并发性的,而不是并行性(和 concurrency is not parallelism)。它们与运行“同时”发生的事情无关。它们是关于安排工作项目的。因此,您应该首先对它们进行推理,就好像它们 运行 在单个内核上运行一样。然后,您可以添加有关如果同时有两个工作项 运行 会发生什么的问题。但是这个问题并没有改变依赖关系的基本问题。