为啥 DispatchGroup 会干扰主队列?

Posted

技术标签:

【中文标题】为啥 DispatchGroup 会干扰主队列?【英文标题】:Why does DispatchGroup interfere with main queue?为什么 DispatchGroup 会干扰主队列? 【发布时间】:2021-06-30 09:04:26 【问题描述】:

我必须停止我的代码一秒钟,以便在继续之前同步服务器数据库。

以下所有代码 sn-ps 都是从主线程运行的。

我先用这个:

// 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://***.com/a/42773392/3657691/https://***.com/a/28821805/3657691/https://***.com/a/49287115/3657691/

【问题讨论】:

那么,你是否从主线程执行第二个 sn-p(产生死锁的那个)? 您滥用DispatchGroup 来强制异步函数变为同步。这是非常糟糕的做法。将// stuff 放入asyncAfter 的闭包中替换group.leave() @VladRusu:正如您在下面正确假设的那样,它确实来自主线程。我会更正我的 OP。 @vadian:是的,正如我所说的,代码不好,但目前更多的是用于测试。最终的生产代码将把延迟的东西保存在完成中。尽管如此,我还是不明白为什么 OP 代码的行为会如此 - 因此是这个问题。 如果组也在主线程上运行(显然是这样)wait 阻塞线程。强烈建议在自己的线程上运行组。再一次——顾名思义——DispatchGroup 用于管理多个任务。 【参考方案1】:

我将假设您在主线程上运行这些 sn-ps,因为这很可能是问题描述中的情况。

调度队列基本上是任务队列,所以一个队列有一些任务入队。让我们看看当你执行 sn -p 生成死锁时主队列上有什么。

    主队列有一个任务正在执行(执行 sn-p 的那个) 然后调用asyncAfter,它将在指定的截止日期后将另一个任务(包含group.leave() 的闭包)排入主队列。

现在正在执行的任务 (1.) 被group.wait() 的调用阻塞,它将阻塞整个主队列,直到它完成执行。这意味着排队的任务 (2.) 必须等到第一个任务完成。您可以在这里看到两个任务会互相阻塞:

第一个 (1.) 将等到第二个 (2.) 释放调度组 第二个 (2.) 将等到第一个 (1.) 完成执行,以便可以安排它

对于第 2 个问题,使用全局队列(或者实际上是我们当前代码正在执行的队列以外的任何其他队列 - 在本例中为主队列)不会阻塞 asyncAfter 任务(显然,因为它被安排在另一个未被阻塞的队列上,因此它有机会被执行)。

串行调度队列也是如此(主队列也是串行队列)。串行调度队列将串行执行它们的任务,一次只有一个。

另一方面,对于并发调度队列,这种情况不会导致死锁,因为asyncAfter 任务不会被等待任务阻塞。这是因为并发调度队列不会等待正在执行的任务完成来调度下一个入队任务。

这甚至是一个很好的练习,可以尝试在串行队列上运行此场景,然后在并发队列上观察差异

【讨论】:

以上是关于为啥 DispatchGroup 会干扰主队列?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 viewDidLayoutSubviews 会干扰动画?

为啥 NSManagedObjectContext 队列在主线程上执行?

CoreBluetooth 代表主队列/主线程?

为啥 AFNetworking POST 多部分请求必须在主队列上同步运行?

为啥必须在主队列上异步调用 resignFirstResponder() 来关闭键盘

在 Swift 中的 Firebase 实时数据库观察方法中具有异步函数的 DispatchGroup