[Interview] DispatchQueues sync vs async Explained

Mohit Dubey
5 min readMay 27, 2024

--

Focusing on sync vs. async operations, execution flow, and practical examples

Photo by frank mckenna on Unsplash

What is Concurrent Programming?

Concurrent programming is a technique where multiple threads are executed simultaneously, allowing for efficient multitasking and improved performance. In iOS, concurrency is achieved by switching between threads based on the complexity and duration of operations.

Achieving Concurrency in iOS

In iOS, you can implement concurrency using several tools:

  • DispatchQueue
  • OperationQueues
  • Combine
  • Async-Await

This article will focus on DispatchQueues, a foundational concept in iOS concurrency.

DispatchQueue Basics

A DispatchQueue manages the execution of tasks in a first-in, first-out (FIFO) order. You can declare a DispatchQueue and optionally provide a label for easier debugging and investigation. Here's how you can create a DispatchQueue:

let newQueue = DispatchQueue(label: “MyNewQueue”)

Getting the Current Thread Name

In concurrent programming, it’s often useful to know the current thread’s name. Here’s a helper property to get the thread name:

extension Thread {
var threadName: String {
if let currentOperationQueue = OperationQueue.current?.name {
return "OperationQueue: \(currentOperationQueue)"
} else if let underlyingDispatchQueue = OperationQueue.current?.underlyingQueue?.label {
return "DispatchQueue: \(underlyingDispatchQueue)"
} else {
let name = __dispatch_queue_get_label(nil)
return String(cString: name, encoding: .utf8) ?? Thread.current.description
}
}
}

Thread.current.threadName

DispatchQueue Sync vs. Async

When working with DispatchQueue, understanding the difference between synchronous (sync) and asynchronous (async) execution is crucial.

  • DispatchQueue.sync: Executes a block of code immediately and waits until the block completes. It doesn’t break the current flow of execution.
  • DispatchQueue.async: Schedules a block of code for execution and returns immediately, allowing the current flow to continue. The block executes at a later time.

Here’s an example illustrating the difference:

let newQueue = DispatchQueue(label: "MyNewQueue") 
newQueue.async {
print("Async task")
}

print("On Main thread")

newQueue.sync {
print("Sync task")
}

Example Scenario: Understanding Execution Flow

Consider the following code snippet:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

//Emoji -> Cmd + Ctrl + Space

let newQueue = DispatchQueue(label: "MyNewQueue")
var value = 0

extension Thread {
var threadName: String {
if let currentOperationQueue = OperationQueue.current?.name {
return "OperationQueue: \(currentOperationQueue)"
} else if let underlyingDispatchQueue = OperationQueue.current?.underlyingQueue?.label {
return "DispatchQueue: \(underlyingDispatchQueue)"
} else {
let name = __dispatch_queue_get_label(nil)
return String(cString: name, encoding: .utf8) ?? Thread.current.description
}
}
}

print(String(repeating: "-", count: 20), "Experiment #1", String(repeating: "-", count: 20))


//This will run on MyNewQueue
newQueue.async {
for i in 0...3 {
value = i

print("Inside first <\(Thread.current.threadName)> \(value) 😜")
}
}

print("I am here on main thread - after first async")

newQueue.async {
for i in 4...6 {
value = i

print("Inside second <\(Thread.current.threadName)> \(value) 😜")
}
}

print("I am here on main thread - after second async")

//This will execute on main thread as it is happening synchronously on main thread
newQueue.sync {
for i in 7...9 {
value = i

print("Inside third <\(Thread.current.threadName)> \(value) 😍")
}
}

print("I am here on main thread - after third sync")

//This will run on MyNewQueue
newQueue.async {
value = 10
print("Inside forth <\(Thread.current.threadName)> \(value) 🤣")
}

print("I am here on main thread - after forth async")

DispatchQueue.main.async {
print("Async Dispatch main <\(Thread.current.threadName)>")
}

Expected output (Most common):
— — — — — — — — — — Experiment #1 — — — — — — — — — —
I am here on main thread — after first async
I am here on main thread — after second async
Inside first <MyNewQueue> 0 😜
Inside first <MyNewQueue> 1 😜
Inside first <MyNewQueue> 2 😜
Inside first <MyNewQueue> 3 😜
Inside second <MyNewQueue> 4 😜
Inside second <MyNewQueue> 5 😜
Inside second <MyNewQueue> 6 😜
Inside third <OperationQueue: NSOperationQueue Main Queue> 7 😍Inside third <OperationQueue: NSOperationQueue Main Queue> 8 😍
Inside third <OperationQueue: NSOperationQueue Main Queue> 9 😍
I am here on main thread — after third sync
I am here on main thread — after forth async
Inside forth <MyNewQueue> 10 🤣
Async Dispatch main <OperationQueue: NSOperationQueue Main Queue>

Understanding the Execution Flow

  1. Experiment #1: This is printed first.
  2. First Async Block: Queued for execution but not executed immediately.
  3. Main Thread Prints: “I am here on main thread — after first async” and “I am here on main thread — after second async”.
  4. Second Async Block: Queued after the first async block.
  5. Sync Block: Executes immediately, but first and second async blocks were added before the sync.
  6. We know the threads start in the same order of their initialisation. So first “0–3” and then “4–6” will be printed.
  7. Then the sync block will start and it will print from “7–9”.
  8. Third Sync Print: “I am here on main thread — after third sync”.
  9. Fourth Async Block: Queued after the sync block.
  10. Fourth Async Print: “I am here on main thread — after forth async”.
  11. Then the remaining thread async or main async will be executed

Key Takeaways

  • Async Execution: Queues tasks and returns immediately.
  • Sync Execution: Executes tasks immediately and waits for completion.
  • FIFO Order: Tasks in DispatchQueue execute in the order they are added, but async tasks may complete in a different order based on their complexity and duration.

Practical Implications in Interviews

Understanding DispatchQueues and the difference between sync and async execution is crucial for technical interviews. Interviewers often test candidates on their ability to predict the output of concurrent code snippets. Mastering these concepts prepares you to tackle such questions confidently.

Conclusion

Concurrent programming is a cornerstone of modern iOS development, and DispatchQueue is a powerful tool in this domain. By mastering DispatchQueue and understanding its execution model, you can write efficient, responsive, and well-structured iOS applications. Keep experimenting with sync and async tasks, and you’ll soon become adept at managing concurrency in your projects.

Next article: [Interview] Concurrent vs. Serial Dispatch Queues in Swift

  • For any queries related to the article or mobile system design interviews, please leave a comment.
  • If you have doubts about any of the steps, drop a comment.
  • Bookmark this article for future reference and quick access.

How can you encourage me?

  • Share this article with friends and colleagues preparing for mobile system design interviews.
  • Show your appreciation by clapping as much as you like; it encourages me to create more content.
  • Follow my profile to stay updated on new articles.

--

--

Mohit Dubey

Associate Director @ VerSe Innovation(Dailyhunt and Josh) | Gen AI, Machine Learning and Deep Learning | mobile full stack | iOS Swift, React Native, KMP | GO