非结构化任务不继承执行上下文

问题描述 投票:0回答:1

我首先要说的是,关于此问题有很多相似但不同的帖子,主要是因为它们提供的示例而难以理解。

所以,我将提供更容易理解的示例代码。

struct ContentView: View {
    @StateObject private var viewModel = ViewModel()

    var body: some View {
        Button("Test") {
            viewModel.runLongTaskOnMainThread()
        }
    }
}

class ViewModel: ObservableObject {
    @MainActor
    func runLongTaskOnMainThread() {
        Task {
            await asyncLongTask()
        }
    }
    
    private func asyncLongTask() async {
        try? await Task.sleep(for: .seconds(5))
        print(#function)
    }
}

我遇到的问题是,我预计点击按钮后,方法

asyncLongTask()
会阻塞主线程,但事实并非如此。

我检查了where它正在执行,发现它在后台线程中。

enter image description here

我认为通过用

runLongTaskOnMainThread()
标记
@MainActor
,可以保证其中运行的任何内容都将在主线程中运行。

然后我意识到这是真的,但这一定意味着任务的创建发生在主线程中,而不是其操作的执行(注意

Task(operation: () -> Success)

所以,我认为将函数重新定义为

@MainActor
    func runLongTaskOnMainThread() {
        Task { @MainActor in
            await asyncLongTask()
        }
    }
然后

会阻塞主线程,但同样不会,因为

asyncLongTask()
是在后台线程中执行的。

然后我发现我能保证它在主线程中运行的唯一方法就是用 @MainActor 标记它:

    @MainActor
    private func asyncLongTask() async {
        try? await Task.sleep(for: .seconds(5))
        print(#function)
    }

enter image description here

这是怎么回事?显然我误解了非结构化任务如何继承其调用者的执行上下文。

我的一个假设是,因为任务内部有一个挂起点(

await asyncLongTask()
),这意味着它可以在不同的线程上恢复执行。意思是,如果有同步代码,它们将在主线程中执行,而不是在
asyncLongTask()
中执行。

swiftui swift-concurrency
1个回答
0
投票

asyncLongTask
调用
Task.sleep
,它不会阻塞调用者线程(与旧版
sleep
API不同)。正如
sleep(nanoseconds:)
文档 所说:

该函数不会阻塞底层线程。

所有

Task.sleep
方法都不起作用。

© www.soinside.com 2019 - 2024. All rights reserved.