I'm running a simulation (SwiftUI app), which has 100 steps.
I need each step to be executed in order.
A first try was to dispatch with delay to schedule each second:
for step in 0..<100 {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(step) * 1.0) {
// simulation code
}
}
Very poor results as 100 running threads are too much load for the system.
So I split in 2 stages:
for bigStep in 0..<10 {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(bigStep) * 10.0 ) {
for step in 0..<10 {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(step) * 1.0) {
// simulation code
}
}
}
}
It works much better, as now there are a max of 20 threads active (in fact I create more levels to limit to a max of 8 concurrent threads).
It addition, it allows to interrupt the simulation before end.
My questions:
- is it the appropriate pattern ?
- Would a timer be better ?
- Other options ?
Should I run in a
global()
instead ofmain
?
No. Apple generally recommends that you avoid the global concurrent queue, for reasons I explain in Avoid Dispatch Global Concurrent Queues.
If an individual step runs quickly, putting it on .main
is fine. Remember that your code running on the main queue is serialised with respect to all other code running on the main queue, so scheduling long-running code there will cause UI responsiveness problems. Your goal should be to limit such code to a few milliseconds, which ensures that all the other stuff running on the main queue has time to run within the current frame (frame times are somewhere between 7 and 33 ms, depending on the device you’re targeting).
If a step takes longer than that then you need a secondary thread. How you get that depends on the structure of your code. The easiest option is to create a Dispatch serial queue or, in modern Swift, an actor.
Keep in mind that code running on a secondary thread can’t update your UI directly, so it’ll have to bounce back to the main thread to do that. And then you have to make sure that that code runs quickly. So, for example, if you have an image to display, render the image on a your secondary thread and then pass the fully rendered image back to the main thread which then just plonks it in the image view.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"