在Task里面使用DispatchQueue可以吗?

Is it ok to use DispatchQueue inside Task?

我现在正在使用 async-await 和 Task 将我的一些代码转换为并发代码。我想知道的一件事是可以在

等任务实例中使用 DispatchQueue
Task {
    await someHeavyStuff()
    DispatchQueue.main.async {
        someUIThreadStuff()
    }
}

据我所知,Task 和 DispatchQueue 在处理异步事物方面几乎没有什么不同的机制,所以我担心同时使用两者会弄乱线程系统。

(我知道在这种情况下我可以使用 MainActor.run {}

你逃脱了 DispatchQueue.main.async { … },但你真的应该放弃这个模式。但是如果你有一个大而复杂的项目,你正在慢慢过渡到 Swift 并发,并且还没有时间清理它,是的,你现在可以不用这个 GCD 调用。

但正确的解决方案是将 someUIThreadStuff 标记为 @MainActor 并停用 DispatchQueue.main.async { … }。这是一个微不足道的修复,MainActor.run { … } 也是如此。在过渡到 Swift 并发的过程中,您可能要处理的所有事情中,这是最简单的事情之一,只需正确地做,并摆脱 GCD API.

在过渡到 Swift 并发时必须特别小心的地方是使用锁和信号量的地方,或者阻塞当前线程的地方。 Swift 并发性无法解释这些,而这些可能是问题的根源。但是对主队列的延迟调度不太可能导致问题,尽管您当然应该在方便的时候尽早将其删除。请参阅 Swift concurrency: Behind the scenes,特别是有关永远不会阻止前进进程的运行时契约的讨论。


当我查看您的代码片段时,我会更关注 Task { … } 以开始 someHeavyStuff。 “startHeavyStuff”这个名字暗示了一些计算量大的东西,阻塞了当前线程。但是 Task { … } 用于在当前当前 actor 上启动异步任务 不适用于后台线程上的 运行 “繁重”任务。现在,someHeavyStuff 正在以某种方式从当前演员那里得到它,然后忽略这个警告。但是请注意,您不要假设 Task { … } 会像 DispatchQueue.global().async { … } 那样工作,因为它不会。


我建议观看 WWDC 2021 Swift concurrency: Update a sample app。它介绍了重构遗留代码的非常实用的练习。