This sample project demonstrates how barrier tasks in GCD/NSOperationQueue and DispatchGroups work.
Add a .barrier
flag to mark the added job as a barrier task. It will prevent any other tasks from running in parallel with it, so any tasks added later will wait until this task finishes work. This is useful if you have some tasks that can mostly be run concurrently, but a subset of those tasks requires exclusive access to some resource (e.g. a database for writing) and other tasks can't run until the resource is released.
dispatchQueue.async(qos: .userInitiated, flags: .barrier) {
// do some work
}
Call addBarrierBlock()
on an OperationQueue
. This is new in macOS 10.15 / iOS 13 and it works exactly like the DispatchQueue
version (as far as I can tell).
operationQueue.addBarrierBlock {
// do some work
}
Dispatch Groups work slightly differently - you can assign a task to a group, and the group keeps a kind of counter that's increased when a task is scheduled and decreased when it finishes work. You can ask to be notified when the counter gets down to 0.
Since new tasks added after you set up the notification also increase the counter, the notification will only fire once the queue is completely empty. This can be useful if you want to run some kind of cleanup tasks after all other tasks finish, including those spawned in the meantime by one of the earlier tasks.
To add a task to a group, either pass the group as a parameter to async
:
let dispatchGroup = DispatchGroup()
dispatchQueue.async(group: dispatchGroup, qos: .userInitiated) {
// do some work
}
Or manually increase and decrease the group counter using the enter()
and leave()
methods:
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
dispatchQueue.async {
// do some work
dispatchGroup.leave()
}
You can use enter()
and leave()
with any blocks of code you run asynchronously, e.g. when running some downloads:
for url in urls {
dispatchGroup.enter()
downloadFile(url) { response in
// ...
dispatchGroup.leave()
}
}
To set up a callback to be run when the queue is empty, use the notify()
method:
let dispatchGroup = DispatchGroup()
// run some tasks
dispatchGroup.notify(queue: queue) {
// do some cleanup after all tasks have finished
}
You can also block the current thread and wait until all tasks finish (possibly with a timeout):
let dispatchGroup = DispatchGroup()
// run some tasks
dispatchGroup.wait()
// continue when work is done