참고사항
https://stackoverflow.com/questions/19822700/difference-between-dispatch-async-and-dispatch-sync-on-serial-queue/19822753
https://stackoverflow.com/a/19822753
When we use a queue, we can either use a global one that is provided to us by Apple, or we can create our own custom ones. It should be noted that global queues should be used cautiously as we wouldn’t want to abuse them.
To get a better understanding of what some of these concepts mean, let’s jump right into the code and create a queue.
let queue = DispatchQueue(label: “queue.1”)
Here we have created a custom queue and assigned a unique label to it. What you call this label is totally arbitrary but it’s good to name it something that is relevant to your app.
We can call different methods on these queues such as async vs sync. These keywords will tell our app how to execute our code.
Here’s an example of our code running synchronously on a background thread versus code that is running on the main thread.
// Background thread
queue.sync {
for i in 0..<10 {
print("🔷", i)
}
}
// Main thread
for i in 20..<30 {
print("⚪️", i)
}
If we run this code we will see something like this:
// prints: 🔷 0🔷 1🔷 2🔷 3🔷 4...⚪️ 20⚪️ 21⚪️ 22⚪️ 23⚪️ 24...
Our program will stop at the for loop that is running on our main thread so that it can execute our block of code in our queue since it is synchronous.
If we change the queue to async, our app will be free to run the code on the main thread, and will also execute the block of code in our queue at the same time, so we get something like this:
// prints: 🔷 0⚪️ 20🔷 1⚪️ 21🔷 2⚪️ 22🔷 3⚪️ 23🔷 4⚪️ 24🔷 ...
Although our main thread is the highest priority in our app, we can also specify the importance of our queue and let our app know how to prioritize our tasks. This specification is referred to as Quality of Service (QOS). QOS is an enum and we can assign the values below to our queues listed in order from highest priority to lowest.
.userInteractive
.userInitiated
.default
.utility
.background
.unspecified
Let’s give this a try and compare two queues by changing their qos
.
let firstQueue= DispatchQueue(label: "queue1", qos: DispatchQoS.userInitiated)
let secondQueue = DispatchQueue(label: "queue2", qos: DispatchQoS.userInitiated)
firstQueue.sync {
for i in 0..<10 {
print("🔷", i)
}
}
secondQueue.sync {
for i in 20..<30 {
print("⚪️", i)
}
}
Here we have set our queues to have the same rank in priority so our app will run these tasks at the same time.
// prints: ⚪️ 20🔷 0⚪️ 21🔷 1⚪️ 22🔷 2⚪️ 23🔷 3⚪️ 24🔷 4 ...
If you try changing your secondQueue
’s qos
to .background
you will get a much different result that will resemble something like this:
// prints: 🔷 0🔷 1🔷 2🔷 3🔷 4...⚪️ 20⚪️ 21⚪️ 22⚪️ 23⚪️ 24...
Our firstQueue
has a higher rank in terms of qos
so our app will prioritize this task and make sure it runs before the secondQueue
.
All of the queues we’ve tested above are known as serial queues. These are queues that will execute each task assigned to it one after another unless we specify otherwise. If we wanted our tasks to run concurrently, meaning have our queues execute various tasks at once, we will have to specify this attribute when we create our queue.
Let’s take a look at our serial queues and this time we will assign numerous tasks to it.
let randomQueue = DispatchQueue(label: "randomQueue", qos: .utility)
By default, our queue is serial since we did not specify it to be concurrent.
randomQueue.async {
for i in 0..<10 {
print("🔷", i)
}
}
randomQueue.async {
for i in 20..<30 {
print("⚪️", i)
}
}
randomQueue.async {
for i in 30..<40 {
print("🔶", i)
}
}
// prints: 🔷 0🔷 1🔷 2🔷 3🔷 4...⚪️ 20⚪️ 21⚪️ 22⚪️ 23⚪️ 24...🔶 30🔶 31🔶 32🔶 33
Here we can see that our queue is performing each task one at a time.
To perform a concurrent queue however, we will add an attribute
to the our randomQueue
.
let randomQueue = DispatchQueue(label: "randomQueue", qos: .utility, attributes: .concurrent)
If we run our we app we will notice that our tasks are now running parallel.
// prints: 🔹 0⚪️ 20🔶 30🔹 1⚪️ 21🔶 31🔹 2⚪️ 22🔶 32🔹 3⚪️ 23🔶 33...
Earlier I mentioned that instead of creating custom queues, the system can provide us with background dispatch queues called global.
let globalQueue = DispatchQueue.global()
globalQueue.async {
// Do something here
}
Global queues can be used in the same way that we use custom queues, but they should not be abused. There a certain traits we cannot customize with this queue but we can always change the qos
.
In addition to using global queues, we can also access main queue depending on our needs.
DispatchQueue.main.async {
// Do something here
}
Main queues are perfect for updating UI and fetching our images, whereas any other heavy task should run on the background thread.
Overall, GCD allows us to multitask in our apps and run code that is synchronous or asynchronous, and serial or concurrently, depending on how we’d like our app to execute certain blocks of code. We can create custom queues, or use Apple’s global queues to perform these various tasks, and the main thread should always be dedicated to updated the UI.
Hope this post was helpful and feel free to leave comments down below!