在MainActor调用后,asyncDetached回落到主线程

人气:945 发布:2022-10-16 标签: async-await swift swift5.5 mainactor

问题描述

我正在试用新的异步/等待功能。我这里的目标是在后台运行test()方法,所以我使用Task.detached;但是在test()期间,我需要调用主线程,所以我使用MainActor。

(我知道孤立地看起来可能很复杂,但它比现实世界中的情况要好得多。)

好的,测试代码如下所示(在视图控制器中):

override func viewDidLoad() {
    super.viewDidLoad()
    Task.detached(priority: .userInitiated) {
        await self.test()
    }
}
@MainActor func getBounds() async -> CGRect {
    let bounds = self.view.bounds
    return bounds
}
func test() async {
    print("test 1", Thread.isMainThread) // false
    let bounds = await self.getBounds()
    print("test 2", Thread.isMainThread) // true
}

第一个print表示我不在主线程上。这正是我所期待的。

但第二个print表示我在主线程上。这不是我所期望的。

感觉好像就因为我调用了MainActor函数,就神秘地回到了主线程中。我以为我将等待主线程,然后在我已经在的后台线程中继续。

这是一个错误,还是我的预期错了?如果是后者,我如何在await期间跳到主线程,但随后又返回到我所在的线程?我认为这正是异步/等待将使之变得简单...?

(在某种程度上,我可以通过在调用getBounds之后再次调用Task.detached来解决这个问题;但在这一点上,我的代码看起来非常像嵌套的GCD,以至于我不得不想知道我为什么要使用异步/等待。)

也许我为时过早,但我将其作为错误提交:https://bugs.swift.org/browse/SR-14756。

更多备注:

我可以通过替换

来解决问题
    let bounds = await self.getBounds()

    async let bounds = self.getBounds()
    let thebounds = await bounds

但这似乎是不必要的详细说明,也不能让我相信最初的现象不是一个错误。

我也可以通过使用演员来解决这个问题,这开始看起来是最好的方法。但同样,这并不能说服我相信我在这里提到的现象不是一个错误。

我越来越确信这是一个错误。我刚刚遇到(并报告)以下情况:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    async {
        print("howdy")
        await doSomeNetworking()
    }
}
func doSomeNetworking() async {
    print(Thread.isMainThread)
}
这将打印howdy,然后第二个print打印true。但是如果我们注释掉第一个打印,剩下的(第二个)print打印false

仅仅添加或删除一条print语句就能改变我们所处的线程吗?这肯定不是故意的。

推荐答案

以下公式很好地解决了整个问题,但我不太愿意发布,因为我不太明白它是如何工作的:

override func viewDidLoad() {
    super.viewDidLoad()
    Task {
        await self.test2()
    }
}
nonisolated func test2() async {
    print("test 1", Thread.isMainThread) // false
    let bounds = await self.view.bounds // access on main thread!
    print("test 2", bounds, Thread.isMainThread) // false
}

我测试了await self.view.bounds调用wazoo,view访问和bounds访问都在主线程上。这里的nonisolated是确保这一点的关键。对此的需求以及伴随而来的await需求对我来说非常令人惊讶,但这似乎都与参与者的性质以及UIView控制器是MainActor的事实有关。

195