`await` 执行后,Swift 任务和 await/async 队列是什么?

3

我正在尝试理解在调用 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. 为什么在第二个示例中同样是在那一行之后的代码却没有在主线程上运行?

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

显然,主要问题在于我想确保在主线程上更新@State变量以使视图正常工作,但是,如果不清楚这个概念,很难设计适当的模式。


这是一篇很好的文章,可以帮助您更好地理解 https://www.avanderlee.com/swift/async-await/。 - AdR
我之前读过那篇文章。是一篇好文章。然而,那个链接并没有解决任何问题。没什么用。 - zumzum
我认为这是因为在SwiftUI中我们没有使用视图模型对象。 - malhal
1个回答

3

“续体”将在哪个线程上运行没有明确的规则。WWDC 2021视频Swift concurrency: Behind the scenes讲解了续体以及悬挂点后的线程可能与之前不同的情况。

通过传统的调试语句或断点来诊断这种情况可能会非常令人沮丧。我遇到过这种情况:插入几行完全不相关的调试代码会改变续体的线程行为。总之,除非您另有规定,否则它可以自由决定续体将在哪个线程上运行,并且有时可能与预期不符。

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

@MainActor
class TaskTestViewModel: ObservableObject {
    ... 
}

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