Concurrency in iOS swift is the method of running the different task at the same time or in random order. Basically its a multiple task running at same time. Most of the task are run in the different threads. Each task runs at the different thread which runs on the particular interval.
Why Concurrency is used in iOS ?
It is basically critical to understand the app at the user end which the app does not slow down or reduce the performance of the app. Basically the impact is on the scrolling of images or loading the data at the user end. If the user experience the slowness of the app he may uninstall the app. To increase the performance of the app and concurrency comes into picture.
Concurrency is segregated into 2 parts
- GCD (Grand Central Dispatch)
- Operational Queues
How Concurrency is Achieved ?
- Time Slicing and Context Switching
- Interruptions
- Scheduling Algorithms
- Latency
- Time Quantum
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 |
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:
- Run heavy tasks in the global queue.
- 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 |
Let me know if you need more details! 🚀🔥
Synchronous and Asynchronous
Synchronous : Execution of task in synchronous manner. here the task are executed one after the another. Untill and unless the 1st task is completed 2nd task is on hold.
GCD execute the task in the form of synchronous and Asynchronous format. In Dispatch Queue Synchronous the task gets executed in the orderly manner .Every task waits until the initial task gets executed and completes it.
// Do the work synchronously
DispatchQueue.main.sync { ... }
Asynchronous : Execution of task in parallel order . the task 2 is executed even if task 1 is not finished.
Dispatch Queue Asynchronous the task execution’s takes place in unorder manner .The code is run in asynchronous manner
// Do the work asynchronously
DispatchQueue.main.async { ... }
What is Queue ?
Lining up of the task to get executed it can be form of synchronous or Asynchronous format
There are 2 types of Queue
- Serial Queue : Serial queue are the queues which are executed one after the another. Task are executed in orderly manner. here task are executed in sequential manner. Execution of task is one at a time.
Example : Consider a following task . Task 1, Task 2 and Task 3 this task has to be executed in serial queues
Task 1 gets executed first then task 2 and then task 3 . Task 2 does not start before Task 1 is completed so same for Task 3 , Task execution takes place in sequential manner .
Pros of serial Queues
- Predictable Executable order
- Prevent Race Condition
2.Concurrent Queue : Task are executed in the same order. The execution of code takes place in parallel format.
Example : Consider a following task. Task 1, Task 2 and Task 3 this task has to be executed in Concurrent queues
Task 1, Task 2 and Task 3 are executed in parallel order . Here Task 2 does not wait for Task 1 to be completed . It execute same order as Task 1 and same for Task 3 all the task are executed in parallel format.
Pros of concurrent Queues
- Faster
- Unpredictable order
Serial/Concurrent affects the destination queue to which task is dispatching.
Sync/Async affects the current thread from which task is dispatching
Difference Between sync
and serial
in GCD (Grand Central Dispatch)
The terms sync (synchronous execution) and serial (serial queue) are related but refer to different concepts in GCD.
1. sync
vs async
(Execution Type)
These control how a task is executed:
Execution Type | Description |
---|---|
sync (Synchronous) |
Waits for the task to finish before continuing. Blocks the current thread. |
async (Asynchronous) |
Starts the task but does not wait for it to finish. Continues execution immediately. |
Example: sync
Blocks Execution
let queue = DispatchQueue.global()
queue.sync {
print("Task 1 started")
sleep(2)
print("Task 1 completed")
}
print("Task 2 started") // This waits until Task 1 completes
✅ Output (Always in order)
Task 1 started
Task 1 completed
Task 2 started
Example: async
Does Not Block
queue.async {
print("Task 1 started")
sleep(2)
print("Task 1 completed")
}
print("Task 2 started") // This runs immediately
✅ Possible Output (Order may vary)
Task 2 started
Task 1 started
Task 1 completed
2. serial
vs concurrent
(Queue Type)
These control where the task runs:
Queue Type | Description |
---|---|
serial queue |
Executes one task at a time, in order. |
concurrent queue |
Executes multiple tasks at the same time. |
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")
sleep(1)
print("Task 2 completed")
}
✅ Output (Always in order)
Task 1 started
Task 1 completed
Task 2 started
Task 2 completed
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")
sleep(1)
print("Task 2 completed")
}
✅ Possible Output (Order may vary)
Task 1 started
Task 2 started
Task 2 completed
Task 1 completed
3. sync
with a Serial Queue (Deadlock Risk)
-
Calling
sync
on the same serial queue causes a deadlock because the queue is waiting for itself to finish.
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
serialQueue.sync {
print("Task 1 started")
serialQueue.sync { // ❌ Deadlock (Waiting on itself)
print("Task 2 started")
}
print("Task 1 completed")
}
This freezes execution because Task 1
is waiting for Task 2
, but Task 2
can’t start until Task 1
finishes.
✅ Fix: Use async
inside sync
instead.
4. sync
with a Concurrent Queue
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
concurrentQueue.sync {
print("Task 1 started")
sleep(2)
print("Task 1 completed")
}
print("Task 2 started") // Runs after Task 1 finishes
✅ Output (Always in order)
Task 1 started
Task 1 completed
Task 2 started
Final Summary
Feature | sync |
async |
serial queue |
concurrent queue |
---|---|---|---|---|
Blocks execution? | ✅ Yes | ❌ No | ✅ Yes (One at a time) | ❌ No (Multiple tasks at once) |
Executes in order? | ✅ Yes | ❌ No | ✅ Yes | ❌ No |
Can cause deadlocks? | ✅ Yes | ❌ No | ✅ Yes (if sync calls itself) |
❌ No |
Use case | When you need order & dependency | When you need performance | When you need thread safety | When you need parallel execution |
🚀 When to Use What?
✅ Use sync
when you need to wait for a task to complete (but avoid inside the same queue).
✅ Use async
for long-running tasks, so they don’t block execution.
✅ Use Serial Queue when tasks depend on each other or need thread safety.
✅ Use Concurrent Queue for independent tasks that can run in parallel.
Let me know if you need more examples! 😊🔥
There are 2 different types of Dispatch Queue
- Main Queue
- Global Queue
Main Queue : This is used to execute the task on main thread .this thread mainly used as UI Thread
DispatchQueue.main.async {
// Perform your async code here
}
Global Queue: This is used to execute the task on the global thread
DispatchQueue.global().async {
// Perform your async code here
}
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