DispatchQueue – Serial & Concurrent

Boring Background of DispatchQueue

A DispatchQueue is an object that allows you to submit tasks to perform asynchronously. The queue will then push work to a given thread within the system’s thread pool.

There are two types of queues: Serial and Concurrent.

Serial Queue: We know that by design, Dispatch Queue’s are FIFO – meaning that they will execute tasks in the order that they are added. Serial queues take this a step further by completing the execution of tasks in the order that they are added.

let serial = DispatchQueue(label: "com.FaisalLearns.Queue")

serial.async {
    print("Faisal jumps")
    print("Faisal is in the air")
    print("Faisal lands on the ground")
}
serialQueue.async {
    print("Bob jumps")
    print("Bob is in the air")
    print("Bob lands on the ground")
}

The output would read something like this:

Faisal jumps
Faisal is in the air
Faisal lands on the ground
Bob jumps
Bob is in the air
Bob lands on the ground

First Faisal jumps – and once he finally lands on the ground, Bob starts to jump. Bob waits for Faisal because we’re using a serial queue.

Now let’s look at an example where we’re using a concurrent queue instead.

let concurrent = DispatchQueue(label: "com.FaisalLearns.Queue", 
attributes: .concurrent)

concurrent.async {
    print("Faisal jumps")
    print("Faisal is in the air")
    print("Faisal lands on the ground")
}
concurrent.async {
    print("Bob jumps")
    print("Bob is in the air")
    print("Bob lands on the ground")
}

Now the output would be along the lines of:

Faisal jumps
Bob jumps
Faisal is in the air
Bob is in the air
Faisal lands on the ground
Bob lands on the ground

As we can see, Faisal and Bob are jumping at the same time. This time, Bob is not waiting for Faisal. Why? Because he’s in a concurrent queue.

What I Learned – CBCentralManager and Queues

I’ve written a bluetooth service that creates a single instance of CBCentralManager to manage the communication between the phone and various bluetooth peripherals.

At first I thought of creating the CBCentralManager instance like this:

self.centralManager = CBCentralManager(delegate: self, queue: nil)

What’s wrong with this? The above will ensure that all central manager update callbacks will be handled on the main thread. This isn’t ideal since we’re doing a decent amount of processing with the incoming data stream, and we’d rather not block the UI thread. That’ll get you a one way ticket to some really poor user experience, and two stars on the App Store.

I did end up creating the CBCentralManager instance like this:

self.centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.global(qos: .background))

Let’s break down why this is wrong based on the boring theory lesson at the beginning of this post. By definition, the global background thread is concurrent. This means that tasks will begin execution in order, but they will not wait for the task before them to complete before starting. Also, because this is a global/public background queue, we’re sharing thread resources with other areas in the application. That’ll slow us down for sure.

When interacting with a BLE (Bluetooth Low Energy) device, it’s important that you communicate on a serial queue. Only one task should be scheduled at a time. Creating several read/write requests on a concurrent queue can quite possibly lead to some issues on the peripheral side of things.

So let’s make the fix.

In my bluetooth service I create an instance of my serial queue:

private let bleQueue: DispatchQueue = DispatchQueue(label: "CentralManager")

and then BOOM!

self.centralManager = CBCentralManager(delegate: self, queue: bleQueue)

Just like that we’ve made our communication protocol much more secure.

Closing Thoughts

Dispatch queues are a powerful resource in iOS. They do the heavy lifting of thread pool management for us. That’s not to say that we don’t have at least some control. We should be cognizant of global/private queue usage, as well as the difference concurrent and serial.

So that’s all for now. Remember, this blog is called faisallearns. I’m still learning, and am yet to flourish into my final form – a TRUE iOS Senior Ninja Jedi Master Guru Philanthropist.

If you find any mistakes in anything that I’ve said, please let me know and I’ll make the appropriate corrections. It’s not cool to spread misinformation.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s