我假设异步任务继承了actor上下文,所以如果我的类标记为@MainActor
,异步任务应该在主线程上运行?我认为我的假设可能有缺陷。
在我的代码中,第一个asyncTest()
方法是在主线程上执行的,但由于某种原因,asyncMap()
是在后台线程上执行的。
这是我测试过的游戏场:
import PlaygroundSupport
import Foundation
@MainActor
class AsyncTest {
nonisolated init() {}
func asyncTest() async {
print(#function, "isMainThread:", Thread.isMainThread)
let test: [String] = ["Hello", "World"]
await test.asyncMap({ string in
return string.uppercased()
})
}
}
extension Sequence {
func asyncMap<T>(
_ transform: (Element) async throws -> T
) async rethrows -> [T] {
print(#function, "isMainThread:", Thread.isMainThread)
var values = [T]()
for element in self {
try await values.append(transform(element))
}
return values
}
}
let asyncTest = AsyncTest()
Task {
await asyncTest.asyncTest()
}
PlaygroundPage.current.needsIndefiniteExecution = true
这使得:
asyncTest() isMainThread: true
asyncMap(_:) isMainThread: false
2条答案
按热度按时间qvtsj1bj1#
在测试这些东西时,最好使用一个真实的的应用程序(甚至可能更好地在设备上运行它)。此外,您的示例掩盖了关键细节。将代码结构减少到更有信息性的内容,消除糟粕并将整个事情从操场上下文中删除,这会产生错误的结果。
试试这个:
在这里,我们已经消除了
Thread
的概念,这在Swift 6中基本上是一个错误。但是通过使用NSLog
进行日志记录,我们可以看到实际的线程数。从中我们可以了解到,asyncTest
异步方法的内部和内部的回调都是在后台线程上执行的。Task {}
调用闭包的内部继承自上下文,因此它在主线程上执行。asyncTest()
的内部在主线程上运行,因为它的类被标记为MainActor。但是当我们来到callMeBack
时,它被声明为“in space”,我们在后台线程上(如我的What determines whether a Swift 5.5 Task initializer runs on the main thread?中所讨论的),因此回调也发生在后台线程上。如果我们想确保
callMeBack
在主线程上运行,我们需要将其声明为@MainActor
。就目前而言,你不能谈论独立于
callMeBack
的回调闭包本身的actor,因为它不是异步的。如果是,你可以将其设置为@MainActor
,如下面的变体:在这个例子中,除了
callMeBack
之外的所有内容都在主线程上运行,您可以通过将callMeBack
标记为@MainActor
来解决这个问题。q3qa4bjr2#
不,
async
方法不“继承”其调用者的actor。(Sequence
,缺少@MainActor
限定符)和函数本身的(它也没有MainActor
限定符)。没有理由asyncMap
会在主actor上运行。它不是与主actor隔离的。因此:
asyncTest
是在一个与主参与者隔离的类型中定义的,因此asyncTest
也与主参与者隔离;asyncMap
是在一个类型(或扩展)中定义的,该类型不与任何特定的参与者隔离,因此这个async
方法不与主参与者隔离;asyncMap
的闭包是在asyncTest
内部创建的,asyncTest
与主参与者隔离,因此这个闭包也与主参与者隔离。值得一提的是,看看你的
asyncMap
想法,值得注意的是Apple已经通过AsyncSequence
提供了map
的异步呈现。为了利用这一点,我们可以使用Swift Async Algorithms包中的async
方法从数组中创建AsyncSequence
。一旦你将这个包添加到你的项目中,你可以做如下事情:不用说,我已经将
uppercased
替换为对某个异步方法的调用(在我的示例中称为foo
)。如果我正在做一些同步的事情,比如uppercased
,我将使用标准的map
并完成它: