提问人:Heuristic 提问时间:5/10/2023 更新时间:5/11/2023 访问量:202
将 DispatchQueue.global().async 转换为同步函数的 async/await
Converting DispatchQueue.global().async to async/await for synchronous function
问:
我正在将我的旧项目从 DispatchQueue 转换为异步等待,我将其包装在全局调度队列中,这是一个耗时的操作:
DispatchQueue.global().async {
self.processed = processData(input)
DispatchQueue.main.async {
render() // reload UI with self.processed
}
}
processData()
是一项耗时的同步操作render()
使用处理后的数据更新 UI,并且它需要位于主线程上
看起来最接近全局队列的东西是使用 ,但是使用它我无法改变主要参与者隔离的属性,例如.Task.detached
self.processed
然后我想这样做:
processData(input: Input) async -> Output {
await withCheckedContinuation { continuation in
DispatchQueue.global().async {
let output = process(input)
continuation.resume(returning: output)
}
}
}
…
let processed = await processData(input)
render()
但感觉就像是为了使用 async/await 而仍然使用 .有什么想法吗?谢谢!DispatchQueue
答:
同样,您可以使用具有后台优先级的任务:
Task.detached(priority: .background) {
let output = await processData(input:yourInput)
await MainActor.run {
render()
}
}
func processData(input: Input) async -> Output {
//do your expensive processing
}
评论
self.output = await processeData(input: yourInput)
是的,理论上您可以使用分离的任务。
只需确保具有此代码的方法与主要参与者以及方法隔离,但方法不是。例如,在一个类型中,它本身是 actor 隔离的,那么你只需要标记为:render
processData
processData
nonisolated
@MainActor
final class Foo: Sendable {
var processed: Output?
func processAndUpdateUI(for input: Input) async throws {
processed = try await Task.detached {
try self.processData(input)
}.value
render()
}
func render() {…}
nonisolated func processData(_ input: Input) throws -> Output {…}
}
但我明确建议不要使用 or 模式。这很脆弱。找到合适的演员的重担不应该落在呼叫者的肩上。只需确保相关方法和属性是 actor 隔离的,编译器将确保您正确调用这些方法,跨越 actor 隔离边界的值为 ,等等。MainActor.run {…}
Task { @MainActor in …}
Sendable
关于后一点的说明。当你在线程之间传递对象时,你必须让编译器知道它们是线程安全的,即 .观看 WWDC 2022 视频:使用 Swift Concurrency 消除数据争用。在 Swift 5.x 中,它不会总是警告你是否是你的类型,所以请考虑将 “Strict Concurrency Checking” 构建设置更改为 “Complete”:Sendable
Sendable
第一次或两次处理类型时,这似乎非常令人困惑,但希望上面的视频会有所帮助。但是,一旦你掌握了一致性,它就会成为第二天性,你会想知道为什么你经历了过去所有那些令人头疼的线程安全问题。Sendable
Sendable
问题在于分离的任务是非结构化并发的。也就是说,如果取消父任务,它不会将取消传播到子任务。
因此,我可能会保留在结构化并发中。例如,我可能会进入它自己的 actor:processData
@MainActor
final class Bar: Sendable {
private let processor = Processor()
var processed: Output?
func processAndUpdateUI(for input: Input) async throws {
processed = try await processor.processData(input)
render()
}
func render() {…}
}
actor Processor {
func processData(_ input: Input) throws -> Output {
while … {
try Task.checkCancellation() // periodically check to make sure this hasn’t been canceled
…
}
return output
}
}
评论