【问题标题】:Why do we call completion on main thread in asynchronous function?为什么我们在异步函数中调用主线程上的完成?
【发布时间】:2021-02-14 22:02:21
【问题描述】:

例如在这个函数中,

func asyncAdd(_ input: (Int, Int),
runQueue: DispatchQueue = DispatchQueue.global(qos: .userInitiated),
completionQueue: DispatchQueue = DispatchQueue.main,
completion: @escaping (Result<Int, SlowAddError>) -> ()) {
runQueue.async {
    let result = input.0 + input.1
    completionQueue.async {
        completion(result)
    }
 }
}

为什么completionQueue,主线程,必须是调用completion的地方。我们不能只在后台线程上调用完成吗?

【问题讨论】:

  • 这取决于完成处理程序的实际作用。通常,当使用回调来修改 UI 时,您会看到这一点,而在 iOS 中,这只能从主线程完成

标签: swift concurrency


【解决方案1】:

是的,但是在主队列上调用完成处理程序通常很方便,因为想要更新 UI 是很常见的,而在错误的队列上调用 UI 更新程序是一个常见的错误。

在主队列上回调通用异步函数不是必需的,甚至也不是普遍推荐的。但是对于某些用例众所周知的系统,它非常方便并且有助于防止错误。

当然,上面的例子没有用,而且不太可能在生产代码中找到。但这不会改变特殊情况下的实用程序。另一个常见的模式是调用者传递一个完成队列。这也很好,特别是对于非常通用的工具(例如使用这种方法的 URLSession)。

【讨论】:

    【解决方案2】:

    “我们不能只在后台线程上调用完成吗?”

    好吧,一般情况下,如果这个后台线程是一个工作线程,我会投“否”票。您必须根据具体情况决定是否可以安全地执行此操作:

    异步执行任务的对象——我们称之为“操作”,可能对执行任务的工作线程(或调度队列)有特殊要求。这可能包括特殊的服务质量级别(参见DispatchQoS)、它是串行队列的事实等。工作线程位于“操作”内部,专门用于运行他们的任务,但可能不适合运行其他任务或功能。

    当操作在其工作线程上调用完成处理程序时,它不再控制其工作线程发生的事情。例如,客户端可以直接执行扩展的 CPU 密集型计算,从而停止操作,因为它的工作线程现在很忙,必须等到客户端的完成处理程序完成。这些计算也可能在根本不适合客户端要求的操作定义的 QoS 上运行。

    因此,所有这些都可能将性能问题引入复杂的异步系统,甚至使其行为变得不可预测。

    一个例子是 URLSession,它不会在其内部工作线程上调用完成处理程序,而是在专用的“回调”OperationQueue 上调用。使用 URLSession 时,您可以提供它,或者 URLSession 会为您创建一个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-27
      • 1970-01-01
      • 1970-01-01
      • 2015-09-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多