我发现这会产生死锁,但我不知道为什么。基本上,我在类中有一个队列,每次应该更改类的状态时,我都会将队列中的任务作为同步任务运行:
private var serialQueue = DispatchQueue(label: "my_mutex_queue")
func changeState() {
serialQueue.sync {
// perform change
}
}
某些状态更改需要调用委托。在这种情况下,无法同步调用任务,因为这将导致死锁。然而,异步调度它也会导致死锁(我们仍然在队列"my_mutex_queue"中的同步任务"changeState"中):
func notifyDelegate() {
serialQueue.async {
// notify delegate
}
}
如果我将委托通知作为异步任务在不同的队列中运行,那么一切都可以正常工作。我在苹果的文档中找不到任何关于为什么在同一队列中调用异步任务会导致死锁的说明。
您不能从serialQueue
正在执行的块中调用serialQueue.sync
。
TL;DR;
以下是我认为可能发生的事情:
- 您通过
serialQueue.async
从notifyDelegate
调度块a - 在块A执行的上下文中,您的委托调用
changeState
,错误地假设当前线程不是serialQueue
的线程 - 在
serialQueue
的调用堆栈上的changeState
方法中,您通过serialQueue.sync
同步调度另一个块B,该块永远无法启动,因为您等待它在当前由serialQueue
执行的先前异步调度的块A中启动
避免这种情况的方法:
- 永远不要在用于同步的专用串行队列中调用公共回调
或
- 不要使用专用队列进行同步,而是使用
os_unfair_lock
、NSLock
或NSRecursiveLock
。它还可能提高性能