【问题标题】:Swift Call async function on main actor from synchronous contextSwift 从同步上下文中调用主要参与者的异步函数
【发布时间】:2022-08-20 18:38:33
【问题描述】:

我试图了解为什么以下代码会引发断言。我想做的是从调用站点在主线程/主要参与者上调用asyncFunc()。我不想用@MainActor 装饰 asyncFunc,因为我希望该函数与线程无关。

func asyncFunc() async -> String? {
     dispatchPrecondition(condition: .onQueue(.main))
     return \"abc\"
}

func callSite() {
     Task { @MainActor in
          await asyncFunc()
     }
}

我的理解是Task { @MainActor ...} 将在 MainActor/主线程上执行以下所有代码。

  • 好吧,闭包和闭包中的任何同步调用都在主线程上,但不一定是内部异步函数。

标签: swift async-await


【解决方案1】:

目前 swift 中的参与者是可重入的,如提案中所述:

Actor 隔离函数是可重入的。当一个actor隔离函数挂起时,重入允许在原来的actor隔离函数恢复之前在actor上执行其他工作,我们称之为交错。重入消除了两个参与者相互依赖的死锁来源,可以通过不不必要地阻塞参与者的工作来提高整体性能,并提供更好地调度(例如)更高优先级任务的机会。

因此,您的asyncFunc 不会在MainActor 上运行,因为它会阻止参与者处理其他高优先级任务。要解决此问题,您可以尝试以下两种方法:

  1. 使您的函数同步,以便它将在MainActor 上运行:
    func syncFunc() -> String? {
         dispatchPrecondition(condition: .onQueue(.main))
         return "abc"
    }
    
    func callSite() {
         Task { @MainActor in
             syncFunc()
         }
    }
    
    1. 或显式标记要在MainActor 上运行的异步函数:
    @MainActor
    func asyncFunc() async -> String? {
         dispatchPrecondition(condition: .onQueue(.main))
         return "abc"
    }
    
    func callSite() {
         Task {
              await asyncFunc()
         }
    }
    

    请注意,这里也适用相同的重入规则,asyncFunc(即dispatchPrecondition)中的同步调用将在MainActor 上执行,而asyncFunc 中的任何异步调用都不会在MainActor 上调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-20
    • 1970-01-01
    • 2012-07-25
    • 1970-01-01
    • 1970-01-01
    • 2015-03-12
    • 2015-03-19
    • 2013-07-12
    相关资源
    最近更新 更多