Difference between Serial and Concurrent Dispatch Queues — (GCD)

Vishal Pethani
4 min readMay 6, 2020

Before directly understand the difference between both queues, Let’s get the understanding of basic.

Apple’s most popular and easy to use a mechanism to write and manage concurrent task — Grand Central Dispatch (GCD). All of the tasks that GCD manages for you are placed into GCD-managed first-in, first-out (FIFO) queues.

How can we achieve the Concurrency? — In general

Threads

Using threads, Yes, we can do the concurrent task using multiple threads. What we called is multithreading. You can use as many threads executing at once as you have cores in your device’s CPU. It’s how you can split your app’s work into multiple threads. Threads are highly optimized by the OS. So, you will have Faster execution, Responsiveness(UI thread), Optimized resource consumption.

More threads, more cores, faster app.

How can I create a thread in iOS?

Well, You don’t need to create a thread explicitly in iOS. The OS will manage all thread creation for you using higher abstraction. Apple gives required the APIs for thread management. The OS managed the thread that when it should and should not allocate or destroy.

Then How Can we do a concurrent task in iOS?

Dispatch Queues

The OS managed the thread itself. You don’t need to create explicitly it. So How Can we do work with thread?

Well, Dispatch queues come in the picture. The way you work with threads is by creating Dispatch Queues. When you create a dispatch queue, OS will assign one or more threads to that queue. It threads is already available, it can be reused. If not, then OS will create them as required.

There are three types of queue

  1. Serial
  2. Global
  3. Custom

Serial queue

Serial queue always runs on a single thread and that’s why it only allows a single task to be executed at a given time. i.e. if you give three async tasks to the serial queue means that each task needs to completely finish before to move another task as there is only one thread available.

Example:

let serialQueue = DispatchQueue(label: “com.queue.Serial”)
for i in 1…5 {
serialQueue.async {
print(“\(i) started”)
Thread.sleep(forTimeInterval: 3)
print(“\(i) completed “)
}
}

You will see below output in the console

1 started
1 completed
2 started
2 completed
3 started
3 completed
4 started
4 completed
5 started
5 completed

Main queue

Another great example is the main queue. The main queue is a serial queue that is responsible for your UI related operation. You can access the queue via DispatchQueue.main

Example:

DispatchQueue.main.async {
Self.tableview.reload()
}

Wow, fairly easy, eh?

The label argument needs to be any unique value so it can be easily identified. The label is what you will see when debugging and it’s helpful to assign it meaningful text. You could simply use the UUID for uniqueness or best to use a reverse domain style name as you do in bundle identifier.

Concurrent Queue

Unlike a Serial queue, a concurrent queue is utilized as many threads as the system has resources for. Threads will be created and released as required on a concurrent queue. It allows multiple tasks to be executed at a given time.

Remember that there is no guarantee that more than one task will run at a time. If your iOS device is completely bogged down and your app is competing for resources, it may only be capable of running a single task.

In order to create a concurrent queue, simply pass in the .concurrent attribute, like so:

Example:

let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)for i in 1...5 {
concurrentQueue.async {
if let url = URL(string: urlString) {
if let data = try? Data(contentsOf: url) {
self.parse(json: data)
}
}
print(“\(i) completed downloading “)
}
print("\(i) executing")
}

You will see below output in the console

1 executing
2 executing
3 executing
4 executing
5 executing
2 completed downloading
4 completed downloading
3 completed downloading
1 completed downloading
5 completed downloading

Apple has provided six different global concurrent queues what they called is Quality of service(QoS)

Quality of Service

When you use the concurrent queue, you will need to tell the OS of priority of task so higher priority task executes first and so on so.

.userInteractive

The .userInteractive QoS is needed for tasks that the user directly interacts with. It would be helpful when you need to UI-updating calculations, animations or anything needed to keep the UI responsive and fast.

.userInitiated

The .userInitiated queue must be used when the user gives action from the UI that needs to happen immediately, but can be done asynchronously. For example, Fetch new data because the user performed pull-to-refresh. Open a document or read from a local database.

.utility

You’ll want to use the .utility dispatch queue for tasks that would typically include a progress indicator such as long-running computations, I/O, networking or continuous data feeds. The system tries to balance responsiveness and performance with energy efficiency. Tasks can take a few seconds to a few minutes in this queue.

.background

You’ll want to use this queue for work that will take significant time, on the order of minutes or more. For tasks that the user is not directly aware of you should use the .background queue. They don’t require user interaction and aren’t time-sensitive. Prefetching, database maintenance, synchronizing remote servers and performing backups are all great examples

.default and .unspecified

There’s a .default option, which falls between .userInitiated and .utility and is the default value of the QoS argument. It’s not intended for you to directly use. The second option is .unspecified, and exists to support legacy APIs that may opt the thread out of a quality of service. It’s good to know they exist, but if you’re using them, you’re almost certainly doing something wrong.

--

--

Vishal Pethani

iOS Developer | Swift |RxSwift | RxCocoa | MVVM | Flutter | Dart