【发布时间】:2021-08-27 00:36:03
【问题描述】:
我正在尝试新的 async/await 东西。我这里的目标是在后台运行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 说我在主线程上am。这不是我所期望的。
感觉好像我只是因为调用了一个 MainActor 函数而神秘地回到了主线程。我以为我会等待主线程,然后在我已经在的后台线程中恢复。
这是一个错误,还是我的期望有误?如果是后者,在await 期间我如何做到主线程,然后又回到我所在的线程?我认为这正是 async/await 可以轻松实现的功能...?
(在某种程度上,我可以通过在调用getBounds 之后再次调用Task.detached 来“解决”这个问题;但那时我的代码看起来非常像嵌套的 GCD,我不得不想知道为什么我' m 完全使用 async/await。)
也许我还为时过早,但我继续将此作为错误提交: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!
仅仅添加或删除一条打印语句如何改变我们所在的线程?这肯定不是故意的。
【问题讨论】:
-
以防万一您认为
Thread.isMainThread以某种方式弄错了,事实并非如此。我知道,因为如果我在await之前在test中谈到self.view.bounds,我会在主线程检查器中崩溃,但如果我在await之后谈到它,我不会崩溃。我们这里确实是上下文切换,我不知道为什么。 -
如果你调用另一个不是 MainActor 的异步方法会发生什么?
-
@EmilioPelaez 如果你这样做会怎样?
-
我在这里注意到的一件事是
.userInitiated优先级,这对于昂贵的工作来说是非常高的。这可能会影响一些调度决策。也许你应该在做昂贵的工作之前再做一个优先级较低的asyncDetached? -
就像
detachedAsync与async一样,优先级与我所描述的结果无关。自己试试吧。
标签: swift async-await swift5.5 mainactor