在同步上下文中从主执行器调用异步函数的Swift方法。

5

我试图理解以下代码为什么会引发断言。我想要在调用现场从主线程/主actor上调用 asyncFunc()。我不想用@MainActor修饰asyncFunc,因为我希望该函数是线程不可知的。

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

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

我的理解是 Task { @MainActor ...} 会在 MainActor/主线程上执行其后面的所有代码。


闭包和闭包中的任何同步调用都在主线程上,但不一定是内部异步函数。 - Cosyn
1个回答

2

目前,Swift中的演员是可重入的,正如提案中所述:

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

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

  1. 使您的函数同步,以便它将在MainActor上运行:
func syncFunc() -> String? {
     dispatchPrecondition(condition: .onQueue(.main))
     return "abc"
}

func callSite() {
     Task { @MainActor in
         syncFunc()
     }
}
  • 或者明确指定您的异步函数在MainActor上运行:
  • @MainActor
    func asyncFunc() async -> String? {
         dispatchPrecondition(condition: .onQueue(.main))
         return "abc"
    }
    
    func callSite() {
         Task {
              await asyncFunc()
         }
    }
    

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

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接