我必须停止我的代码一秒钟,以便在继续之前同步服务器数据库。
下面的所有代码片段都从主线程运行。
我首先使用这个:
// code 1
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
// dummy
}
// stuff
结果显然不符合预期,因为内容是立即执行的。我希望代码块之后延迟运行(这不太好,但这是有原因的)。
// code 2
let group = DispatchGroup()
group.enter()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
group.leave()
}
group.wait()
// stuff
死锁!
问题 1:为什么主队列在没有 DispatchGroup 的情况下工作并与与 DispatchGroup 一起锁定?
// code 3
let group = DispatchGroup()
group.enter()
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
group.leave()
}
group.wait()
// stuff
这按预期工作!
问题 2:为什么 DispatchGroup 需要全局队列来延迟运行?
我在这里读到了这个:
https://stackoverflow.com/a/42773392/3657691/ https://stackoverflow.com/a/28821805/3657691/ https://stackoverflow.com/a/49287115/3657691/
最佳答案
我假设您正在主线程上运行这些代码片段,因为问题描述中很可能就是这种情况。
调度队列基本上是任务队列,因此队列中有一些任务排队。让我们看看当您执行产生死锁的代码片段时主队列上有什么。
- 主队列正在执行一个任务(执行代码片段的任务)
- 然后,您调用
asyncAfter
,它将在指定的截止日期之后将另一个任务(包含group.leave()
的闭包)排入主队列。
现在,正在执行的任务 (1.) 被调用 group.wait()
所阻塞,并且它将阻塞整个主队列,直到它完成执行。这意味着排队任务 (2.) 将必须等待第一个任务完成。您可以在此处看到这两个任务将相互阻塞:
- 第一个 (1.) 将等待,直到第二个 (2.) 释放调度组
- 第二个 (2.) 将等待第一个 (1.) 完成执行,以便进行调度
对于问题 2,使用全局队列(或者实际上是除我们当前代码正在执行的队列之外的任何其他队列 - 在本例中为主队列),不会阻塞 asyncAfter
任务(显然,因为它被调度到另一个未被阻塞的队列上,因此它有机会被执行)。
这对于串行调度队列来说是正确的(主队列也是串行队列)。串行调度队列将串行执行其任务,即一次仅执行一个任务。
另一方面,对于并发调度队列,这种情况不会导致死锁,因为 asyncAfter
任务不会被等待任务阻塞。这是因为并发调度队列不会等待正在执行的任务完成来调度下一个排队的任务。
这甚至是一个很好的练习,可以尝试在串行队列上运行此场景,然后在并发队列上运行以观察差异
关于ios - 为什么DispatchGroup会干扰主队列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68191691/