当您在 Swift 的同步队列中异步分派任务时会发生什么?

What happens when you dispatch a task asynchronously inside a sync queue in Swift?

我发现这会产生死锁,但我不明白为什么。基本上,我在 class 中有一个队列,每次 class 的状态应该改变时,我 运行 队列中的那个任务作为同步任务:

private var serialQueue = DispatchQueue(label: "my_mutex_queue")
func changeState() {
  serialQueue.sync {
    // perform change
  }
}

某些状态更改需要调用委托。在这种情况下,任务不能被同步调用,因为它会造成死锁。但是,异步调度它也会导致死锁(我们仍在队列“my_mutex_queue”中的同步任务“changeState”中):

func notifyDelegate() {
        serialQueue.async { 
            // notify delegate
        }
}

如果我运行将委托通知作为异步任务放在不同的队列中,那么一切都会按预期进行。 我在 Apple 的文档中找不到任何说明为什么在同一队列中调用异步任务会导致死锁。

您不能从 serialQueue 正在执行的块中调用 serialQueue.sync

TL;DR;

以下是我认为可能发生的情况:

  1. 您通过 serialQueue.asyncnotifyDelegate 安排了区块 A。
  2. 在块 A 执行的上下文中,您的委托调用 changeState,错误地假设当前线程不是 serialQueue 的线程。
  3. changeState 方法,在 serialQueue 的调用堆栈上,您通过 serialQueue.sync 同步安排另一个块 B,它永远不会启动,因为您等待它在当前由 serialQueue.
  4. 执行的先前异步调度的块 A 中启动

避免这种情况的方法:

  1. 切勿在用于同步的专用串行队列中调用 public 回调。

  1. 不要使用私有队列进行同步,使用os_unfair_lockNSLockNSRecursiveLock代替。它还可能会提高性能。