let queue = DispatchQueue(label: "maintenance", qos: .utility)
queue.async {
while true {
guard let task = GET_PENDING_TASK() else {
SLEEP(interval)
continue
}
PROCESS(task)
}
}
Is this a reasonable pattern? If yes, which sleep function should I call? If not, what would be a reasonable pattern to use to accomplish something like this?
I vaguely remember that you are not supposed to sleep in a DispatchQueue, but I think it was on a WWDC video? Is this true? The 6500 lines of generated headers for Dispatch don't seem to mention anything relevant.
You can use DispatchQueue.asyncAfter variations for sleeping behaviour.
Youâre not suppose to sleep in DispatchQueue because sleeping over it will prevent other work on the same queue from being executed. Since they donât expect you to sleep, doing so may have other implication as well.
You can also use DispatchSourceTimer, since it seems you want it to repeat. Do note that you need to keep the timer alive. Further, you can specify the DispatchQueue the timer will operate on.
Thanks, I kind of remembered that, but there is a lot of lore around Dispatch spread between videos, mailing list archives and forum posts. Thanks for the verification.
I'm currently using Timers to approximate this, but I was looking for a more straightforward solution. I guess the answer is "This is not a reasonable pattern for Dispatch"?
There is nobody submitting tasks in my scenario. Imagine for example a background queue that scans cache files and deletes them if they are older than X hours.
As long as there are expired files it should delete them, if there are none, it sleeps and tries again later.
In such case, it wouldnât be a problem to sleep if nobody is using the queue (yet again, Iâm unsure of the performance implication; we could wait patiently for someone in the knows to passby :-).
Becareful in a setup with a hierarchy of queue though, itâll block all the way up to the root queue. This is also part of the reason I prefer async or timer over sleep in queue, sleepâs more prone to error when queue structure change.
Note: I do personally prefer DispatchSourceTimer over NSTimer, for the actual difference I left it to the glorious google search. One important difference I recall is that DispatchSourceTimer uses DispatchQueue while NSTimer uses Runloop so you may have preference if the system already use one of those.
This is from a few years ago, but I once had problems in a codebase that had many independent concurrent queues where submitted blocks (the number of which depended on user data) waited on semaphores (we have since moved on to a more sensible design). This led to the worker threads Dispatch manages internally being blocked, to which Dispatch would respond by spawning more threads, which eventually led to crashes due to running into thread count limits (iirc, might have been stack space?). The same thing might happen when submitting many blocking work items to a single concurrent queue, I imagine.
Either way, be careful about blocking. If you have a fixed number of queues/work items, it should probably be fine, but you still need to think about what you are doing with your resources.
I guess the answer is "This is not a reasonable pattern for Dispatch"?
Correct. ahti explains this but I want to go into a little more detail.
Dispatch is responsible for mapping work on queues to worker threads. The goal is to have one thread put core. However, if a thread blocks then Dispatch may need to spawn extra worker threads to pick up the slack while the thread is blocked. Dispatch has a bunch of logic to handle this but in the worst case scenario you can experience a thread explosion.
In your case the best option would be to use the Thread API, so that your thread does not tie up a Dispatch worker thread. That API is part of Swiftâs Foundation because itâs still useful. And with the block syntax introduced in the 2016 OS releases, itâs no harder to use than Dispatch.
Thread {
while true {
guard let task = GET_PENDING_TASK() else {
SLEEP(interval)
continue
}
PROCESS(task)
}
}
It feels like for a lot of Dispatch questions the answer is "you are doing it wrong, go watch these videos".
In any case, thanks for the pointer on using the Tread API. That's basically exactly what I want. To be honest I though that the API was discouraged, nice to hear it is still recommended.