了解嵌套 DispatchQueue 调用的死锁

Understanding deadlock on nested DispatchQueue calls

我有2个类似的案例,第一个

let queue1 = DispatchQueue(label: "queue1")
let queue2 = DispatchQueue(label: "queue2")

queue1.sync {
   print(1, Thread.current)

   queue2.sync {
      print(2, Thread.current)
   }

   print(3, Thread.current)
}

// result
1 <NSThread: 0x600001700140>{number = 1, name = main}
2 <NSThread: 0x600001700140>{number = 1, name = main}
3 <NSThread: 0x600001700140>{number = 1, name = main}

和第二个

queue1.sync {
   print(1, Thread.current)

   DispatchQueue.main.sync {
      print(2, Thread.current)
   }

   print(3, Thread.current)
}

// result
1 <NSThread: 0x60000170c8c0>{number = 1, name = main}
deadlock

正如我们所见,第一种情况使用了 2 个串行队列,所有块都在主线程上调用,没有死锁。第二种情况使用 2 个串行队列(如果它们是主队列,则为一个)并在主线程上调用,但现在这会导致死锁。那么有什么区别呢?

当你用.sync()提交任务时,当前线程(无论是哪个线程)都必须等到任务完成,因为那是.sync()的重点。鉴于线程被阻止继续,一旦允许提交它的队列 运行 任务,它实际上会 运行 在调用 .sync() 的线程上。 GCD 不需要从它的线程池中招募一个线程,因为有一个已知线程可用。 (这不能保证,因此。这是一种优化。但它解释了为什么在你的第一个片段中主线程上的所有任务 运行。)

不过,在你的第二个片段中,我的短语 "once the queue […] is allowed to run the task" 生效了。主队列已经 运行 处理了一些事情:对 queue1.sync() 的调用。主队列不允许 运行 另一个任务,直到另一个任务完成,因为它是串行的。 (请注意,主队列和主线程虽然密切相关,但不是一回事。)因此,主队列上 运行ning 的任务被阻塞等待 运行ning 的任务在主队列上完成。因此,僵局。