"@MainActor in"和"MainActor.run"之间有什么区别吗?"

13

这两种方式有什么区别吗:

Task { await MainActor.run { ... } }

Task { @MainActor in ... }

1
一开始,我会怀疑第一个创建一个简单的任务,仅仅是“蹦床”到主要的执行者,而第二个则直接转换到主要的执行者。在调试器中花费一些时间可能可以验证这一点。 - Scott Thompson
1个回答

10

一个区别是一个使用同步闭包,而另一个使用async闭包。具体来说,run使用同步闭包(即,body不是async):

public static func run<T>(resultType: T.Type = T.self, body: @MainActor @Sendable () throws -> T) async rethrows -> T where T : Sendable

这非常适合在您正在处理其他角色的情况下运行一系列三个方法,所有这些方法都与主角色隔离,但希望使用单个上下文切换而不是三个。
但是,在Task.init中,operationasync,这使得它成为一个稍微更灵活的机制:
public init(priority: TaskPriority? = nil, operation: @escaping @Sendable () async -> Success)

因此,为了说明区别,请考虑:

Task { @MainActor in
    statusText = "Fetching"
    await viewModel.fetchData()
    statusText = "Done"
}

但是你不能在MainActor.run中使用await

Task {
    await MainActor.run {            // Cannot pass function of type '@Sendable () async -> ()' to parameter expecting synchronous function type
        statusText = "Fetching"
        await viewModel.fetchData()
        statusText = "Done"
    }
}

你需要再插入一个 Task。(!)
Task {
    await MainActor.run {
        Task {
            statusText = "Fetching"
            await viewModel.fetchData()
            statusText = "Done"
        }
    }
}

我其实很少同时使用这两种模式,但这是它们之间的一个区别。


3
就我个人而言,如果方法或属性在正确的对象上定义,它基本上会使上述模式变得无关紧要。在我的看法中,负担应该在定义点而不是调用点。虽然这些模式在某些边缘情况下很有用(例如减少上下文切换的数量),但通常只是代码异味。 - Rob

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