I’m converting my old project from DispatchQueue to async await, there’s a time consuming operation which I wrapped in the global dispatch queue:
DispatchQueue.global().async {
self.processed = processData(input)
DispatchQueue.main.async {
render() // reload UI with self.processed
}
}
processData()
is a time consuming synchronous operationrender()
updates UI with processed data, and it need to be on main thread
Looks like the closest thing to global queue is to use Task.detached
, but using it I can’t mutate main actor-isolated properties e.g. self.processed
.
Then I thought about doing this:
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()
But it feels like doing it for the sake for using async/await while still use DispatchQueue
. Any thoughts? Thanks!
2
Answers
You can use Task with background priority likewise:
Yes, you theoretically can use a detached task.
Just make sure that the method which has this code is isolated to the main actor, as well as the
render
method, but that theprocessData
method is not. E.g., in a type that is, itself, is actor isolated then you just need to markprocessData
asnonisolated
:But I would explicitly advise against the
MainActor.run {…}
orTask { @MainActor in …}
patterns. That is brittle. The burden for getting onto the right actor should not fall on the shoulders of the caller. Just make sure that the relevant methods and properties are actor-isolated and the compiler will ensure that you call these methods correctly, that values crossing actor-isolation boundaries areSendable
, etc.A note on this latter point. When you pass objects between threads, you have to let the compiler know that they are thread-safe, i.e.,
Sendable
. See WWDC 2022 video Eliminate data races using Swift Concurrency. In Swift 5.x, it will not always warn you if your types areSendable
or not, so consider turning changing the “Strict Concurrency Checking“ build setting to “Complete”:The first time or two you deal with
Sendable
types, it can seem awfully confusing, but hopefully the above videos will be helpful. But once you masterSendable
conformance, it will become second nature and you’ll wonder why you suffered through all those thread-safety headaches of yesteryear.The problem is that detached tasks are unstructured concurrency. I.e., if you cancel the parent task, it will not propagate the cancelation to the child tasks.
So, I might remain within structured concurrency. E.g., I might move
processData
into its own actor: