Feb 26, 2025 study Material

GCD for main and Global Thread

Table of Contents

GCD (Grand Central Dispatch)

GCD (Grand Central Dispatch) is a Low level API basically used to manage the concurrency of the application. It manages the heavy task at the background.

We use DispatchQueue for the operation for the GCD . DispatchQueue is the method object which is used to manage the task execution in serial or concurrent manner basically in FIFO order.

Thread :

Thread is basically the running the task at the different core. it is basically running or execution of code .

In Swift’s Grand Central Dispatch (GCD), tasks can run on different threads. Two important ones are:

  • Main Thread (DispatchQueue.main): this is basically called as UI thread where we show the data to the user from the application
    • Runs on the UI thread.
    • Used for updating UI elements (buttons, labels, views, etc.).
    • Blocking the main thread will freeze the UI.
DispatchQueue.main.async {
    print("Updating UI on the main thread")
    myLabel.text = "Updated!"
}
  • Global Queue (Background Threads) (DispatchQueue.global()) : This is the thread basically used for the logic calculation or service call at the background.
    • Runs tasks on background threads.
    • Used for heavy operations like network requests, database queries, and image processing.
    • Does not block the UI thread.
QoS Level Priority Use Case
.userInteractive Highest UI animations, quick tasks
.userInitiated High Data loading, quick computations
.default Medium Normal tasks (default)
.utility Low Large downloads, parsing
.background Lowest Data sync, backups

Quality of Service

  • User Interactive
  • User Initiated
  • Utility
  • Background

User Interactive : it is used when there is user interaction related task or Animation related work takes place

DispatchQueue.global(qos: .userInteractive).async{
    print("User Interactive task")
}

User Initiated : It is to be used when we required immediate result. basically during the scrolling or searching.

DispatchQueue.global(qos: .userInitiated).async  {
    print("User Initiated task")
}

Utility : This is used for long running task basically in downloading where user is aware of the proccess which is not at the high priority.

DispatchQueue.global(qos: .utility).async  {
    print("User Utility task")
}

Background : This is used to run the task at the background. This task are not visible to the user

DispatchQueue.global(qos: .background).async  {
    print("Background thread")
}

There are 2 more Quality of Service which are rarely used they are

  • Default
  • Unspecified

Example: Running a Task on a Background Thread

DispatchQueue.global(qos: .background).async {
    print("Performing a background task")

    DispatchQueue.main.async {
        print("Returning to main thread for UI update")
    }
}

βœ… Use Case:

  • Run heavy tasks on a global queue.
  • Switch back to main queue for UI updates.

3. Main Thread vs. Global Queue Example

print("Task 1 - Running on \(Thread.isMainThread ? "Main" : "Background")")

DispatchQueue.global(qos: .userInitiated).async {
    print("Task 2 - Running on \(Thread.isMainThread ? "Main" : "Background")")

    DispatchQueue.main.async {
        print("Task 3 - Running on \(Thread.isMainThread ? "Main" : "Background")")
    }
}

βœ… Possible Output:

Task 1 - Running on Main
Task 2 - Running on Background
Task 3 - Running on Main

πŸ”Ή Task 1 runs on the main thread.
πŸ”Ή Task 2 runs on a background thread.
πŸ”Ή Task 3 returns to the main thread.


4. When to Use Each?

Feature Main Thread (DispatchQueue.main) Global Queue (DispatchQueue.global())
UI Updates βœ… Yes ❌ No
Network Calls ❌ No βœ… Yes
Image Processing ❌ No βœ… Yes
File Reading ❌ No βœ… Yes

5. Avoid Blocking the Main Thread

❌ Bad Example (Blocking Main Thread)

print("Start")

DispatchQueue.main.sync {
    print("This will cause a deadlock!")
}

print("End")

🚨 This will freeze the app! The main queue is waiting for itself, causing a deadlock.


6. Correct Usage: Running Background Tasks and Updating UI

DispatchQueue.global(qos: .userInitiated).async {
    let result = heavyComputation()

    DispatchQueue.main.async {
        updateUI(with: result)
    }
}

βœ… Best Practice:

  1. Run heavy tasks in the global queue.
  2. Switch back to DispatchQueue.main for UI updates.

πŸš€ Final Summary

Feature DispatchQueue.main (Main Thread) DispatchQueue.global() (Global Queue)
Runs on UI Thread? βœ… Yes ❌ No
Blocks UI? ❌ No (unless sync is used) ❌ No
Handles Heavy Work? ❌ No βœ… Yes
Use Case UI Updates Background Processing

Swift Interview Questions & Answers on Main Thread & Global Thread


1. What is the difference between the main thread and a global thread in Swift?

βœ… Answer:

  • Main Thread (DispatchQueue.main)
    • Runs on the UI thread.
    • Used for UI updates, user interactions.
    • Blocking the main thread will freeze the UI.
  • Global Queue (DispatchQueue.global())
    • Runs tasks in the background (multithreaded).
    • Used for heavy operations like network requests, database queries, and image processing.
    • Improves performance by preventing UI lag.

Example:

DispatchQueue.global().async {
    print("Background Task")
    
    DispatchQueue.main.async {
        print("Back to Main Thread for UI Update")
    }
}

2. Can we update the UI from a background thread?

βœ… Answer:
No, UI updates must be done on the main thread.
Updating UI from a background thread will cause unexpected behavior or crashes.

Example (Wrong):

DispatchQueue.global().async {
    myLabel.text = "Updating from background" // ❌ UI update in background thread (BAD)
}

Example (Correct):

DispatchQueue.global().async {
    let result = heavyComputation()

    DispatchQueue.main.async {
        myLabel.text = result // βœ… UI update on the main thread (GOOD)
    }
}

3. What is the Quality of Service (QoS) in Global Queues?

βœ… Answer:
Quality of Service (QoS) defines task priority in DispatchQueue.global().

QoS Priority Use Case
.userInteractive Highest UI animations, instant feedback
.userInitiated High Data loading, quick computations
.default Medium Normal tasks (default)
.utility Low Large downloads, background tasks
.background Lowest Syncing, maintenance tasks

Example:

DispatchQueue.global(qos: .background).async {
    print("Background Task Running...")
}

4. What happens if we call sync on DispatchQueue.main?

βœ… Answer:
It causes a deadlock because the main thread waits for itself to finish.

Example (Deadlock 🚨):

print("Start")

DispatchQueue.main.sync {
    print("This will never execute!") // ❌ Deadlock occurs here
}

print("End") // ❌ This will never run

βœ… Fix: Use async instead of sync to prevent deadlocks.


5. What is the difference between DispatchQueue.main.sync and DispatchQueue.main.async?

βœ… Answer:

Function Blocks Execution? Runs Task On
DispatchQueue.main.sync βœ… Yes (Waits) πŸ›‘ Dangerous – Can cause Deadlock
DispatchQueue.main.async ❌ No (Non-blocking) βœ… Main Thread (Safe for UI Updates)

βœ… Example: Safe UI Updates with async

DispatchQueue.main.async {
    print("UI Update on Main Thread")
}

6. How do you perform a long-running task in the background and then update the UI?

βœ… Answer:
Use DispatchQueue.global().async for background work and DispatchQueue.main.async for UI updates.

Example:

DispatchQueue.global(qos: .userInitiated).async {
    let data = fetchDataFromServer()

    DispatchQueue.main.async {
        updateUI(with: data)
    }
}

7. What is the difference between a Serial and a Concurrent Queue in GCD?

βœ… Answer:

Queue Type Description
Serial Queue Executes tasks one at a time, in order.
Concurrent Queue Executes multiple tasks simultaneously.

Example: Serial Queue

let serialQueue = DispatchQueue(label: "com.example.serialQueue")

serialQueue.async {
    print("Task 1 started")
    sleep(2)
    print("Task 1 completed")
}

serialQueue.async {
    print("Task 2 started")
}

βœ… Output (Always in Order)

Task 1 started
Task 1 completed
Task 2 started

Example: Concurrent Queue

let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)

concurrentQueue.async {
    print("Task 1 started")
    sleep(2)
    print("Task 1 completed")
}

concurrentQueue.async {
    print("Task 2 started")
}

βœ… Possible Output (Order may vary)

Task 1 started
Task 2 started
Task 1 completed

8. Can you call DispatchQueue.main.sync inside a background thread?

βœ… Answer:
Yes, calling sync on DispatchQueue.main from a background thread is safe.

Example (Safe):

DispatchQueue.global().async {
    print("Background Task Started")

    DispatchQueue.main.sync {
        print("Back to Main Thread for UI Update")
    }
}

βœ… Output:

Background Task Started
Back to Main Thread for UI Update

🚨 Warning: Never call sync inside DispatchQueue.main itself (deadlock risk).


9. What happens if you create a custom DispatchQueue?

βœ… Answer:
A custom queue can be serial (default) or concurrent.

Example: Serial Custom Queue

let mySerialQueue = DispatchQueue(label: "com.example.mySerialQueue")

mySerialQueue.async {
    print("Task 1 started")
    sleep(1)
    print("Task 1 completed")
}

mySerialQueue.async {
    print("Task 2 started")
}

βœ… Output (Always in order)

Task 1 started
Task 1 completed
Task 2 started

10. What is the main advantage of using DispatchQueue.global(qos:)?

βœ… Answer:
It allows you to execute tasks asynchronously on a background thread, improving app performance by preventing UI blocking.


πŸš€ Summary

Concept Main Thread (DispatchQueue.main) Global Queue (DispatchQueue.global())
Runs on UI Thread? βœ… Yes ❌ No
Blocks UI? ❌ No ❌ No
Handles Heavy Work? ❌ No βœ… Yes
Can Execute Concurrently? ❌ No βœ… Yes
Use Case UI Updates Background Tasks

πŸ”₯ Key Takeaways for Interviews:

  • UI updates must always be on the main thread (DispatchQueue.main).
  • Heavy tasks should run in the background (DispatchQueue.global()).
  • Calling sync on the main thread causes a deadlock!
  • Use async to avoid blocking the main thread.
  • Use QoS to control task priority in DispatchQueue.global(qos:).

Advanced Swift Interview Questions & Answers on Main & Global Threads (GCD)


11. What happens if you call DispatchQueue.global().sync inside another DispatchQueue.global().async?

βœ… Answer:

  • It works fine because DispatchQueue.global() is a concurrent queue and does not block itself.

Example (Safe Execution)

DispatchQueue.global().async {
    print("Task 1 - Started")

    DispatchQueue.global().sync {
        print("Task 2 - Synchronous Execution")
    }

    print("Task 1 - Completed")
}

βœ… Expected Output:

Task 1 - Started
Task 2 - Synchronous Execution
Task 1 - Completed

πŸ”Ή Why does it work?

  • DispatchQueue.global() is a concurrent queue, so calling sync inside async does not cause a deadlock.

12. What is the difference between DispatchQueue.global().asyncAfter and DispatchQueue.main.asyncAfter?

βœ… Answer:

  • asyncAfter delays execution without blocking the current thread.
  • DispatchQueue.global().asyncAfter executes a background task after a delay.
  • DispatchQueue.main.asyncAfter executes a UI-related task after a delay.

Example: Background Execution (Global Queue)

DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
    print("Background task executed after 3 seconds")
}

Example: UI Execution (Main Queue)

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    print("UI update after 2 seconds")
}

βœ… Use Cases:

  • Use DispatchQueue.global().asyncAfter for delayed background tasks.
  • Use DispatchQueue.main.asyncAfter for delayed UI updates.

13. What are the different ways to create a queue in GCD?

βœ… Answer:
You can create a custom dispatch queue in different ways:

1️⃣ Serial Queue (Default)

let serialQueue = DispatchQueue(label: "com.example.serialQueue")

serialQueue.async {
    print("Task 1 - Executing serially")
}

serialQueue.async {
    print("Task 2 - Executing serially")
}

βœ… Tasks execute one after another.


2️⃣ Concurrent Queue

let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)

concurrentQueue.async {
    print("Task 1 - Executing concurrently")
}

concurrentQueue.async {
    print("Task 2 - Executing concurrently")
}

βœ… Tasks execute at the same time.


3️⃣ Main Queue (For UI)

DispatchQueue.main.async {
    print("Main thread execution")
}

4️⃣ Global Queue (Background Processing)

DispatchQueue.global(qos: .background).async {
    print("Background task executing...")
}

14. How do you prevent race conditions in a concurrent queue?

βœ… Answer:
Use DispatchQueue.sync on a serial queue to synchronize access.

πŸ”Ή Problem: Race Condition

var sharedResource = 0

DispatchQueue.global().async {
    sharedResource += 1
}

DispatchQueue.global().async {
    sharedResource += 1
}

print(sharedResource) // ❌ Unpredictable output

πŸ”Ή Solution: Use a Serial Queue

let syncQueue = DispatchQueue(label: "com.example.syncQueue")

syncQueue.sync {
    sharedResource += 1
}

syncQueue.sync {
    sharedResource += 1
}

print(sharedResource) // βœ… Predictable output: 2

βœ… Why?

  • The serial queue ensures tasks execute one by one, avoiding conflicts.

15. How do you use DispatchGroup to wait for multiple tasks to complete?

βœ… Answer:
A DispatchGroup allows multiple asynchronous tasks to run in parallel and notifies when all are finished.

Example:

let dispatchGroup = DispatchGroup()

dispatchGroup.enter()
DispatchQueue.global().async {
    sleep(2)
    print("Task 1 completed")
    dispatchGroup.leave()
}

dispatchGroup.enter()
DispatchQueue.global().async {
    sleep(1)
    print("Task 2 completed")
    dispatchGroup.leave()
}

dispatchGroup.notify(queue: DispatchQueue.main) {
    print("All tasks completed!")
}

βœ… Expected Output:

Task 2 completed
Task 1 completed
All tasks completed!

πŸš€ dispatchGroup.notify runs after all tasks finish.


16. How do you implement a Barrier Block in GCD?

βœ… Answer:
A barrier block ensures a task executes alone, preventing simultaneous reads and writes.

Example:

let concurrentQueue = DispatchQueue(label: "com.example.barrierQueue", attributes: .concurrent)

concurrentQueue.async {
    print("Reading data...")
}

concurrentQueue.async(flags: .barrier) {
    print("Writing data safely!")
}

concurrentQueue.async {
    print("Reading data again...")
}

βœ… Expected Behavior:

  • Read operations execute concurrently.
  • Write operation (barrier block) executes alone.

17. How do you cancel an ongoing task in GCD?

βœ… Answer:
Use DispatchWorkItem to cancel a task before execution.

Example:

let workItem = DispatchWorkItem {
    print("Executing work item")
}

DispatchQueue.global().asyncAfter(deadline: .now() + 2, execute: workItem)

workItem.cancel() // ❌ Task won't execute

βœ… The task will never run because it’s canceled before execution.


18. How do you implement a Timer using GCD?

βœ… Answer:
Use DispatchSourceTimer for accurate timers.

Example:

let timer = DispatchSource.makeTimerSource()

timer.schedule(deadline: .now(), repeating: 1.0)
timer.setEventHandler {
    print("Timer fired!")
}

timer.resume()

βœ… This prints β€œTimer fired!” every second.


19. What is the difference between DispatchSemaphore and DispatchGroup?

βœ… Answer:

Feature DispatchSemaphore DispatchGroup
Purpose Controls access to resources Waits for multiple tasks
Blocks Execution? βœ… Yes (If no resources available) ❌ No (Asynchronous)
Use Case Limiting concurrent threads Waiting for multiple async tasks

Example: Limiting Threads with DispatchSemaphore

let semaphore = DispatchSemaphore(value: 2)

DispatchQueue.global().async {
    semaphore.wait()
    print("Task 1 executing")
    sleep(2)
    semaphore.signal()
}

DispatchQueue.global().async {
    semaphore.wait()
    print("Task 2 executing")
    sleep(2)
    semaphore.signal()
}

DispatchQueue.global().async {
    semaphore.wait() // Will wait until one task finishes
    print("Task 3 executing")
    semaphore.signal()
}

βœ… Limits concurrency to 2 threads.


πŸš€ Final Takeaways

1️⃣ Main Thread (DispatchQueue.main) handles UI updates.
2️⃣ Global Queue (DispatchQueue.global()) runs background tasks.
3️⃣ Avoid deadlocks by never calling sync inside DispatchQueue.main.
4️⃣ Use DispatchGroup to wait for multiple async tasks.
5️⃣ Use DispatchSemaphore to limit concurrent operations.
6️⃣ Use DispatchWorkItem to cancel a task before execution.


πŸ’‘ Want more deep-dive Swift concurrency topics? πŸš€πŸ”₯

Table of Contents

β†’ Index