Swift Async - Task.detached 在哪个线程上运行?

Posted

技术标签:

【中文标题】Swift Async - Task.detached 在哪个线程上运行?【英文标题】:Swift Async - what thread does Task.detached run on? 【发布时间】:2021-06-09 10:40:40 【问题描述】:

我编写了一个全局函数,允许我在 DispatchSemaphore 的帮助下同步等待异步任务的完成(仅用于实验目的!)。

///
/// synchronously waits for the completion of an asynchronous closure
/// - parameter handler: the task to run, asynchronously 
/// - note: don't use this in production code, it violates
/// the Swift runtime contract of "forward" progress, it's never
/// safe to block a thread and wait for another thread to unblock it
/// (on a single core system, this may hang forever)
///
func sync(handler: @escaping () async -> Void) 
    let sema = DispatchSemaphore(value: 0)
    // default `Task.Priority` to `asyncDetached`  is `nil`
    Task.detached 
        await handler()
        sema.signal()
    
    // blocks the current thread, waiting for the async Task to finish
    sema.wait()

我注意到,如果我只使用 Task 块来启动异步任务,就会出现死锁,大概是因为 sema.wait() 阻塞了当前线程,从而阻止了 Task 块运行(正常 Task块似乎继承了当前线程),所以它会永远等待。

使用Task.detached 似乎不会阻塞线程,使上面的代码工作。 这似乎与分配了分离调用的Task.Priority? 有关。 调用Task.detached时,默认参数值为nil。 这可以自定义为不同的优先级,指示调度 QoS。

因此,我的问题是:Task.Priority? = nil 与什么线程相关?它似乎与它从哪里启动的线程不同。 Task.Priority? = nil 是否指示 Swift 运行时始终在不同的线程上运行?

编辑

继续提问 - TaskTask.detached 在线程方面有何不同?

【问题讨论】:

【参考方案1】:

优先级不会影响应用端的优先级。提示主机优先响应多个请求。宿主完全可以忽略这一点。

请参阅此answer 了解更多信息。

因此任务优先级与线程无关。 你的任务每次都会被调度,Task.Priority 指的是没有线程并且与线程无关。

【讨论】:

【参考方案2】:

我建议使用 GCD 队列和DispatchGroups,而不是使用信号量和 asyncDetached。它们重量轻,非常易于使用。你只是

创建一个 DispatchGroup(let group = DispatchGroup(),调用 group.notify(queue:work:) 提交一个块,以便在任务组完成时运行。 每次开始您想要等待的任务时,请致电 group.enter(), 完成任务后致电group.leave()

一旦任务完成,系统将调用您在notify(queue:work:) 调用中提交的块。这里的所有都是它的。您可以提交 0 个、一个或多个任务。如果您尚未提交任何内容,或者它们都已完成,您的通知关闭会立即触发。如果某些任务仍在运行,则在您的任务调用 group.leave()

之前不会调用您的通知闭包

【讨论】:

这在现实中是一个可接受的用例,但在我的示例中,我实际上想要代码阻止它的线程,直到工作完成(在另一个线程上)然后继续同步,而不是在异步 notify 块中通知。在我的示例中,我可以将 DispatchSemaphore 替换为 DispatchGroup,但在这种情况下结果将是相同的。 同步代码在多线程上运行的意义何在?与在单个线程上执行所有操作相比,您似乎没有获得任何好处,而且阅读和维护更加复杂。 为什么投反对票?这似乎是一个 A/B 问题。 (不是我的反对票)。问题是 Swift async/await 代码不能同步执行,它必须发生在 async 上下文中。例如,在处理 actors 时需要异步代码(通过 async/await 隔离和管理访问),因此在实际用例中可能需要同步调度 async 块(从同步上下文开始时,例如)。

以上是关于Swift Async - Task.detached 在哪个线程上运行?的主要内容,如果未能解决你的问题,请参考以下文章

Swift 中的 async/await ——代码实例详解

swift Swift 3使用这篇精彩文章中的简单示例更新了代码:http://alisoftware.github.io/swift/async/error/2016/02/06/async-err

图解 Swift async/await

Swift之深入解析异步函数async/await的使用与运行机制

Swift 5.5 async let - 错误:表达式为“异步”但未标记为“等待”

如何并行使用 Swift async/await