Swift Thread and Grand Central Dispatch
Apple offers two ways for multitasking in iOS development: Grand Central Dispatch (GCD) and NSOperation. In this essay, we shall discuss GCD. If you need a review on the fundamentals of concurrency in iOS development, read this article.
GCD was introduced in iOS 4 and gives developers more freedom when using concurrency in their apps.
The basic goal of GCD is to bring thread management closer to the operating system. It abstracts threads away from the developer, reducing the number of details he must consider. In essence, GCD determines which thread will be used to complete a given task.
\
How it works?
GCD performs system-submitted tasks using closure queues. From Apple documentation, Queues, also known as dispatch queues, are objects that handle the execution of tasks either sequentially or concurrently.\
GCD provides three type Queues :
- Main Queue : Serial
- Global Queue: Concurrent (Parallel)
- Custom Queue : Serial / Concurrent (Parallel)\
When we create an application, a Main Thread
is also created. It is associated with the application. The application contains only one main thread.\
Main Queue
runs on the Main Thread
. If the main thread becomes blocked, the application will crash.\
The main queue in GCD is serial
, not concurrent. The main queue handles jobs serially, which means they are executed one at a time, in the order they were added to the queue.
GCD Main Queue
+------------+ +------------+ +------------+
| | | | | |
| Task 1 +---->| Task 2 +--->| Task 3 |
| | | | | |
+------------+ +------------+ +------------+
\
DispatchQueue.main.async{
// main thread will not get blocked
// all UI update
}
DispatchQueue.main.sync{
// main thread will block after task complete then release
}
\
Global Queue is a concurrent Queue
that executes tasks based on the priority of task. the way we set up its importance is by using quality of service (Qos).
Four main types Qos and one is default.
- userInteractive
- userInitiated
- default
- utility
- background
\
userInteractive
userInteractive
tasks that have a userinteactive quality of service run on the main thread. they execuated immediately to ensure great user experience.
let userInteractiveQueue = DispatchQueue.global(qos: .userInteractive)
userInteractiveQueue.async {
// Perform a task that requires immediate user interaction
}
\
userInitiated
userInitiated
often used for responsive user interactions and tasks that require timely responses.
let userInitiatedQueue = DispatchQueue.global(qos: .userInitiated)
userInitiatedQueue.async {
// Perform a task initiated by the user that should complete quickly
}
\
default
default
is commonly used for general-purpose tasks that don’t have specific QoS requirements. It represents a moderate priority level.
let defaultQueue = DispatchQueue.global(qos: .default)
defaultQueue.async {
// Perform a task with default priority
}
utility
utility
is used for tasks that are considered non-time-critical and can run in the background without impacting the user experience significantly. It is typically used for long-running or background tasks that are not high-priority.
let utilityQueue = DispatchQueue.global(qos: .utility)
utilityQueue.async {
// Perform a background task with utility priority
// api calling perform
}
background
background
is used for tasks that are considered low-priority and can run in the background without affecting the user experience. It’s typically used for long-running or non-critical background tasks that should not interfere with the responsiveness of the application.
let backgroundQueue = DispatchQueue.global(qos: .background)
backgroundQueue.async {
// Perform a background task with low priority
// like database vacuuming, maintenance
}
Custom Queue
Custom Queue
can be either serial or concurrent. most oftern custom queues are execuated on a global queue.
In the absence of an attributes the custom queue will be a serial queue.
// Create a custom serial queue
let customSerialQueue = DispatchQueue(label: "serialQueue")
// Perform a task on the custom serial queue
customSerialQueue.async {
print("Task 1 is running on the custom serial queue.")
}
// Perform another task on the same custom serial queue
customSerialQueue.async {
print("Task 2 is running on the custom serial queue.")
}
Attributes are what define the behavior of the queue. When creating a custom queue, there are two major attributes to consider, with one option being to make the queue concurrent.
// let cq = DispatchQueue(label: "con", attributes: [.initiallyInactive, .concurrent])
// Create a custom concurrent queue
let customConcurrentQueue = DispatchQueue(label: "concurrentqueue", attributes: .concurrent)
// Perform tasks concurrently on the custom concurrent queue
customConcurrentQueue.async {
print("Task 1 is running on the custom concurrent queue.")
}
customConcurrentQueue.async {
print("Task 2 is running on the custom concurrent queue.")
}
\
What are async
and sync
?
Async (Asynchronous)
- Asynchronous execution means that a task is initiated, but the program doesn’t wait for it to complete before moving on to the next task.
- Will not block current queue
- Does not wait for task to finish
- Multiple items at the same time
\
Sync (Synchronous)
- Synchronous execution means that a task is initiated, and the program waits for it to complete before moving on to the next task.
- Multiple items one at a time
- Block the current thread until tasks is finished
In summary
, while tasks submitted to global dispatch queues are assigned specific QoS classes that determine their priority, the system's thread pool manages the threads used to execute these tasks. Tasks in a specific QoS class may run on separate threads depending on system resource availability.