是否在"等待"执行后Swift任务和等待/异步队列



我正在努力理解调用Task { ... }时的规则,以及在该任务中,根据线程调用await时的规则。

此示例有效:

struct TaskTestView: View {

let url = URL(string: "https://www.google.com")!
@State private var message = "Loading..."
var body: some View {
Text(message)
.task {
/// on MAIN THREAD
do {
var receivedLines = [String]()
for try await line in url.lines {
/// on MAIN THREAD
receivedLines.append(line)
message = "Received (receivedLines.count) lines"
}
} catch {
message = "Failed to load"
}
}
}
}

这不是:

struct TaskTestView: View {
@StateObject var model = TaskTestViewModel()

var body: some View {
Text(model.message)
.task {
/// - here it is on main thread
await model.refresh()
/// - here it is NOT on main thread
print("after refresh: on main?")
}
}
}
class TaskTestViewModel:ObservableObject {

let url = URL(string: "https://www.google.com")!
@Published private(set) var message = "Loading..."

func refresh() async {

do {
var receivedLines = [String]() // on main thread
for try await line in url.lines {
receivedLines.append(line) // NOT on main thread
message = "Received (receivedLines.count) lines"
}
} catch {
message = "Failed to load"
}

}

}
  1. 为什么在第一个示例中for try await line in url.lines {行之后的主线程上运行代码
  2. 为什么代码没有在同一行之后的主线程上运行,而是在第二个示例中运行

如果不运行代码并设置断点来检查我所在的线程,我如何知道这些问题的答案?

显然,这里的主要问题是,我想确保在main上更新@State变量,以便视图正常工作,但是,没有明确的概念会使设计正确的模式变得困难。

关于"continuation"将在哪个线程上运行,没有明确的规则。WWDC 2021视频Swift并发:幕后讨论了延续,以及挂起点之后的线程可能与之前的线程有所不同。

试图用传统的调试语句或断点来诊断这一问题可能是一项令人沮丧的工作。我遇到过这样的情况:插入几行完全不相关的调试代码会改变连续线程的行为。最重要的是,除非你另有指示,否则它可以自由决定继续运行哪个线程,有时它可能会超出预期。

但是,如果你想确保你的可观察对象在主线程上更新,你可以使用@MainActor限定符:

@MainActor
class TaskTestViewModel: ObservableObject {
... 
}

最新更新