March 15, 2021
The 7 Different Forms of OperationQueue in Swift
DispatchGroup, DispatchWorkItem, DispatchWorkItemFlags, DispatchSemaphore, addDependency, and addBarrierBlock at OperationQueue

By Eric Yang
3 min read
The OperationQueue class is the high-level abstraction on GCD (Grand Central Dispatch). It executes the queued Operation objects based on their priority and readiness. This article describes the beauty of different approaches to managing async tasks in OperationQueue.
1. Use DispatchGroup of OperationQueue
To wait on groups of queued tasks, we can use the DispathGroup of OperationQueue. According to Apple's documentation:
"Dispatch groups are a way to block a thread until one or more tasks finish executing. You can use this behavior in places where you cannot make progress until all of the specified tasks are complete."
- The group is created to wait on the
asyncBlocktasks. - The queue added each async task to the group to process the results when they are done.
The result is as following:
async block #1 start
async block #2 start
async block #2 end
async block #1 end
async block #3 start
async block #3 endasync block #1 start
async block #2 start
async block #2 end
async block #1 end
async block #3 start
async block #3 end2. The enter(), leave(), and wait() of DispatchGroup
To manage the async tasks, we can also register the enter, leave, and wait events of DispathGroup.
The idea is simple enough, right? Create a group, register a bunch of enter events, and leave when the task is complete. The group will automatically do what you need it to do when all works are done!
The result is as following:
async block #1 start
async block #2 start
async block #1 end
async block #2 end
async block #3 start
async block #3 endasync block #1 start
async block #2 start
async block #1 end
async block #2 end
async block #3 start
async block #3 end3. Use DispatchWorkItemFlags Barrier
The DispatchWorkItemFlags is a set of behaviors for a work item. It contains assignCurrentContext, barrier, detached, enforceQoS, and inheritQos etc. The barrier indicates the work item submitted acts as a barrier.
"Work items submitted prior to the barrier execute to completion, at which point the barrier work item executes. Once the barrier work item finishes, the queue returns to scheduling work items that were submitted after the barrier."
The result is as following:
async block #1 start
async block #2 start
async block #1 end
async block #2 end
async block #3 start
async block #3 endasync block #1 start
async block #2 start
async block #1 end
async block #2 end
async block #3 start
async block #3 end4. Use DispatchWorkItem With the Barrier Flag
The DispatchWorkItem is performed on a dispatch queue or with a dispatch group with encapsulated work.
The result is as following:
async block #1 start
async block #2 start
async block #1 end
async block #2 end
async block #3 start
async block #3 endasync block #1 start
async block #2 start
async block #1 end
async block #2 end
async block #3 start
async block #3 end5. Use DispatchSemaphore
DispatchSemaphore contains the value parameter to control the number of threads that can be executed at the given moment. The value is increased by 1 when calling the signal() method of DispatchSemaphore and is decreased by 1 when calling the wait() method.
- The init value of
DispatchSemaphoreis 2. It allows two threads to execute at the same time. - Value is 1 when calling the first
semaphor.wait()at line 4. - The
aysncBlock(id: 1)is executed since value ≥ 0. - Value is 0 when calling the second
semaphor.wait()at line 10. - The
aysncBlock(id: 2)is executed since value ≥ 0. - Value is -1 when calling the second
semaphor.wait()at line 16. - The
aysncBlock(id: 3)is WAITING since value < 0. aysncBlock(id: 1)is finished after two seconds, and the value is increased by 1, since thesemaphor.signal()at line 6 is executed.- The
aysncBlock(id: 2)is executed since the value is 0 now.
The result is as following:
async block #1 start
async block #2 start
async block #1 end
async block #2 end
async block #3 start
async block #3 endasync block #1 start
async block #2 start
async block #1 end
async block #2 end
async block #3 start
async block #3 end6. Use addDependency() of OperationQueue
As per Apple's documentation:
"The receiver is not considered ready to execute until all of its dependent operations have finished executing. If the receiver is already executing its task, adding dependencies has no practical effect. This method may change the
isReadyanddependenciesproperties of the receiver."
The result is as following:
async block #2 start
async block #1 start
async block #2 end
async block #1 end
async block #3 start
async block #3 endasync block #2 start
async block #1 start
async block #2 end
async block #1 end
async block #3 start
async block #3 end7. Use addBarrierBlock() of OperationQueue
When using addBarrierBlock() to add the task, the operation queue will be as follows:
- All tasks submitted before the barrier are to be executed.
- The barrier will be executed when all tasks submitted are completed.
- And then back to the tasks submitted after the barrier.
The result is:
async block #1 start
async block #2 start
async block #1 end
async block #2 end
async block #3 start
async block #3 end
async block #4 start
async block #4 endasync block #1 start
async block #2 start
async block #1 end
async block #2 end
async block #3 start
async block #3 end
async block #4 start
async block #4 endConclusion
The OperationQueue plays a fundamental role in iOS. It's important to understand not only what the OperationQueue is but also how it works and how we work with it.
I hope you enjoyed this piece and the beauty of how we can easily manage async tasks.