Grand Central Dispatch (GCD) is a set of frameworks written by Apple to enable code to execute applications in a multi-core, concurrent manner.

A DispatchQueue is a class defined in the Dispatch framework that can be used to perform multithreaded operations.

Before using it, we need to understand the basic concepts. I will introduce them briefly and then gradually introduce them in detail according to the content of the explanation. The purpose is to make it easy for readers to get involved.

PS: If there is any mistake in reading, please advise me, thank you!

Synchronous and asynchronous execution

As shown in figure. The difference between synchronous and asynchronous is that the thread waits for the synchronous task to complete; The thread does not wait for the asynchronous task to complete and then continues to perform other tasks/operations.

Reading Guide:

In this article, “task” refers to the entire code block in sync {} and Async {}, and “operation” refers to each instruction (code) executed in the “task”. Since the main thread does not have a “task”, each piece of code executed on the main thread is collectively called an “operation”.

Serial and concurrent queues

In GCD, tasks are managed and ordered by ** queues (serial or concurrent) ** on a thread automatically assigned by the system.

When tasks are executed in Serial queues, they are executed in a fixed order, with one task executed before the next (meaning that Serial queues can only execute one task at a time). When a task is executed in a Concurrent queue, the task can be executed simultaneously (in effect, constantly switching threads to execute the task in a very short amount of time).

Both serial and concurrent queues execute tasks in first-in-first-out (FIFO) order. The execution flow of tasks is shown in the figure below:

Example 1 – Performing synchronization on a serial queue (sync) tasks

// Create a queue (default is a serial queue, no additional parameters required)
let queue = DispatchQueue(label: "Serial.Queue")

print("thread: \(Thread.current)")

queue.sync {
    (0..<5).forEach { print("rool-1 -> \ [$0): \(Thread.current)") }
}

queue.sync {
    (0..<5).forEach { print("rool-1 -> \ [$0): \(Thread.current)")}}/** thread: 
      
       {number = 1, name = main} rool-1 -> 0: 
       
        {number = 1, name = main} rool-1 -> 1: 
        
         {number = 1, name = main} rool-1 -> 2: 
         
          {number = 1, name = main} rool-1 -> 3: 
          
           {number = 1, name = main} rool-1 -> 4: 
           
            {number = 1, name = main} rool-2 -> 0: 
            
             {number = 1, name = main} rool-2 -> 1: 
             
              {number = 1, name = main} rool-2 -> 2: 
              
               {number = 1, name = main} rool-2 -> 3: 
               
                {number = 1, name = main} rool-2 -> 4: 
                
                 {number = 1, name = main} */
                
               
              
             
            
           
          
         
        
       
      
Copy the code

There is nothing to explain, the results must follow the normal order, one after the other. Synchronous execution is to wait until one task is complete before continuing to execute the next task.

It is important to note that the main Thread prints the same result as Thread,current in the synchronous task. In other words, the system assigns the main Thread to the synchronous task when it executes, because the synchronous task will make the Thread wait for its completion. Since the synchronous task will wait, there is no need to create another Thread.

About the main thread and main queue

When an application starts, a thread is created by the system and runs immediately at the same time. This thread is usually called the main thread of the application.

The system also provides us with a special serial queue named dispatchqueue.main {}. By default, we write code in the main queue and all tasks in the main queue are executed in the main thread.

Example 2 – Performing asynchrony on a serial queue (async) tasks

let queue = DispatchQueue(label: "serial.com")

print("thread: \(Thread.current)")

(0..<50).forEach {
    print("main - \ [$0)")
    // Let the thread sleep for 0.2s in order to simulate time-consuming operations.
    Thread.sleep(forTimeInterval: 0.2)
}

queue.async {
    (0..<5).forEach {
        print("rool-1 -> \ [$0): \(Thread.current)")
        Thread.sleep(forTimeInterval: 0.2)
    }
}

queue.async {
    (0..<5).forEach {
        print("rool-2 -> \ [$0): \(Thread.current)")
        Thread.sleep(forTimeInterval: 0.2)}}/** thread: 
      
       {number = 1, name = main} main - 0 main - 1 main - 2 ... 49 RoOL-1 -> 0: 
       
        {number = 3, name = (null)} rool-1 -> 1: 
        
         {number = 3, name = (null)} rool-1 -> 2: 
         
          {number = 3, name = (null)} rool-1 -> 3: 
          
           {number = 3, name = (null)} rool-1 -> 4: 
           
            {number = 3, name = (null)} rool-2 -> 0: 
            
             {number = 3, name = (null)} rool-2 -> 1: 
             
              {number = 3, name = (null)} rool-2 -> 2: 
              
               {number = 3, name = (null)} rool-2 -> 3: 
               
                {number = 3, name = (null)} rool-2 -> 4: 
                
                 {number = 3, name = (null)} */
                
               
              
             
            
           
          
         
        
       
      
Copy the code

As you can see, the thread must wait for its current operations (including hibernation) to complete before continuing to execute the async task. The tasks are also executed sequentially, because a serial queue can only execute one task before proceeding to the next.

In other words, when the asynchronous tasks in the serial queue are executed, the system opens other threads for them and only one Thread. Because the serial queue can only execute one task at the same time, there is no need to open multiple threads.

About putting threads to sleep

Thread.sleep lets the current Thread pause any action for 0.2s.

Notice I’m talking about the current thread, don’t mistake it for stopping the entire application, it’s not. If the thread of the current task stops, it will not affect the other threads executing the task.

PS: That is, in the above synchronization task, the thread.sleep method called for testing (but still called for testing and verification) has no effect because the tasks are all on one Thread and are executed in a fixed order.

Example 3 – Performing asynchrony on a serial queue (asyncII) task

let queue = DispatchQueue(label: "serial.com")
print("1: \(Thread.current)")
queue.async { print("2: \(Thread.current)")}print("3.\(Thread.current)")
queue.async { print("4: \(Thread.current)")}print("5: \(Thread.current)")

/** 1: 
      
       {number = 1, name = main} 3: 
       
        {number = 1, name = main} 2: 
        
         {number = 3, name = (null)} 5: 
         
          {number = 1, name = main} 4: 
          
           {number = 3, name = (null)} */
          
         
        
       
      
Copy the code

At this time, the printing order is not fixed, but it will start from 1, and the printing result may be 12345, 12354, 13254, 13245, 13524, 13254… Why is that? Let’s look at some of the concepts and then review them.

Relationship between queues and tasks

The first thing I want to do is explain the concept of synchronous and asynchronous, and since synchronous or asynchronous can also mean the same or different, it needs a reference object to know whether they are the same or different relative to this object.

In GCD, their reference is our main thread (dispatchqueue.main). That is, if the task is synchronous, it is executed on the main thread; If the task is asynchronous, it is executed in another thread.

This explains why a serial queue starts a thread when it’s executing an asynchronous task, which means it’s not executing on the main thread, except that a serial queue only starts one thread, whereas a concurrent queue starts multiple threads.

And a synchronization task, whatever queue or task it is, as long as it’s a synchronization task, it’s executed on the main thread.

  • Asynchronous tasks

    The asynchronous task says, “I’m going to execute the task, give me a thread to execute.”

    The app says, “Ok! I’ll create another thread for you to execute. Wait, what queue are you in?”

    The asynchronous task says, “Serial queue.”

    The application says, “Since this is a serial queue, and all tasks in a serial queue are executed in a fixed order and can only execute one task before proceeding to the next (meaning that a serial queue can only execute one task at a time), I’ll just assign you one thread! All tasks in your queue, including you, are executed sequentially on this thread.”

    The asynchronous task says, “What if I’m in a concurrent queue?”

    The application says, “If I’m in a concurrent queue, where all the tasks in the queue can be executed at the same time, I’ll assign you multiple threads so that each task can be executed on different threads at the same time.”

  • Synchronization task

    The synchronization task says, “I’m going to execute the task, give me a thread to execute.”

    The application says, “Since it’s a synchronous task that’s equivalent to executing it on the main thread, I’ll give you the main thread to execute it!”

    Synchronous task says, “I’m badly paid.”

Relationship between tasks and threads

There are only two types of tasks, synchronous tasks and asynchronous tasks. No matter what queue a synchronous task is in, it will make the currently executing thread wait for its completion. For example:

// The current thread prints main-1
print("main-1")

// The thread will wait until it encounters a sync task,
// No further operations are performed until the sync task is completed.
//
// Serial or concurrent queues
queue.sync {
    (0..<10).forEach {
        print("sync \ [$0): \(Thread.current)")
        Thread.sleep(forTimeInterval: 0.5)}}/ / wait! The thread waits for sync to complete and then continues to print main-2.
print("main-2")

/** main-1 sync 0: 
      
       {number = 1, name = main} sync 1: 
       
        {number = 1, name = main} sync 2: 
        
         {number = 1, name = main} sync 2 ... 9 main-2 */
        
       
      
Copy the code

If it is an asynchronous task, no matter what queue it is in, the current thread will not wait for it to complete. For example:

// The current thread prints main-1
print("main-1")

// The thread encounters an async task,
// Then the thread will not wait for it to complete and will continue to perform other operations.
//
// Serial or concurrent queues
queue.async {
    (0..<20).forEach { print("async \ [$0)")}}// The thread opening time is about 90 milliseconds, plus the preparation and printing time of the loop,
// It is given 200 subtleties to test the order of execution between the thread in the async task and the current thread.
Thread.sleep(forTimeInterval: 0.0002000)

// No waiting! The thread does not wait for the async execution to complete before printing main-2
print("main-2")
Copy the code

The results of printing may be slightly different, but you must print from main-1 first. Although main-2 is executed after async, async will execute first, but the current thread does not wait for the completion of its execution mechanism, so when it is executed at a certain moment, if the thread needs to print main-2, it will print main-2. It is also possible that main-2 executes first and then executes tasks in async at a certain point (it takes time to start a thread).

In other words, the current thread and the async thread do not block each other during execution. The result of this run is as follows:

/** main-1 async 0 async 1 async 2 main-2 async 3 async 4 async 5 ... * /
Copy the code

PS: How do I know that the opening time of the thread is about 90 subtle? Because I looked at the description in thread cost.

review

This explains the order of execution in the previous example, so let’s review:

let queue = DispatchQueue(label: "serial.com")
print("1: \(Thread.current)")
queue.async { print("2 -\(Thread.current)")}print("3.\(Thread.current)")
queue.async { print("4: \(Thread.current)")}print("5: \(Thread.current)")
Copy the code

Although the execution order is not fixed, there are certain rules to follow. Because it is a serial queue, 1, 3 and 5 must be executed in order in the main thread, and 2 and 4 must be executed in order in the async thread.

Example 4 – Serial queue deadlocks

First, concurrent queues do not have deadlocks; Second, in a serial queue, only sync {sync {}} and async {sync {}} will be deadlocked, the internal Sync closure will never be executed, and the program will crash, for example:

queue.sync {
    print("1")
    queue.sync { print("2")}print("3")}// Prints "1"

queue.async {
    print("1")
    queue.sync { print("2")}print("3")}// Prints "1"
Copy the code

A closer look at the code above shows that deadlocks occur only if sync {} is applied internally, so what does sync mean? This means that the current thread will wait for the synchronization task to complete. The problem is that the sync task is nested within another task (sync {sync {}}), so there are two tasks.

Because the serial queue is to finish the current task, then continue to execute the next task. The internal sync {} must wait for the external sync {} to complete. Because the internal task is synchronous, it blocks the thread currently performing the external sync {}, forcing the current thread to wait for the internal sync {} to complete. The problem is that if the external sync {} does not complete, the internal sync {} does not execute, and the result is waiting. No one can continue to execute, causing a deadlock.

Since the thread waits for the internal sync task to complete and restricts the serial queue to one task at a time, the internal sync {} task can never execute until the external sync {} task completes, while the external thread waits for the internal sync {} task to complete. The external sync {} cannot be executed.

Summary: Because serial queues can only perform one task at a time, it means that the thread can only perform the current task before moving on to the next one anyway. A synchronous task, on the other hand, makes the thread wait for it to complete. The problem is that I can neither execute it first nor wait for it, with the result that the external task never completes and the internal task never starts.

For the second async {sync {}} deadlock, the principle is the same, do not be confused by its external async {}, internal sync {} will also block its thread execution, blocking the result of the external async {} async {} can not complete. The internal sync {} will never be started.

Sync {async {}} and async {async} are nested structures for the other two tasks in the serial queue, for example:

queue.sync {
    print("task-1")
    queue.async {
        (0..<10).forEach {
            print("task-2: \ [$0) \(Thread.current)")
            Thread.sleep(forTimeInterval: 0.5)}}print("task-1 - end")}/** 1 task-1 - end task-2: 0 
      
       {number = 3, name = (null)} task-2: 1 
       
        {number = 3, name = (null)} task-2: 2 ... 9 * /
       
      

queue.sync {
    print("task-1")
    queue.async {
        (0..<10).forEach {
            print("task-2: \ [$0) \(Thread.current)")
            Thread.sleep(forTimeInterval: 0.5)}}print("task-1 - end")}/** 1 task-1 - end task-2: 0 
      
       {number = 3, name = (null)} task-2: 1 
       
        {number = 3, name = (null)} task-2: 2 ... 9 * /
       
      
Copy the code

Although there is no longer a deadlock, the order of execution is slightly different. As you can see, the application completes the external task before executing the internal task. This is because the internal async {} task is no longer blocking the current thread, and because the serial queue can only execute the current task first before the next task, it is natural to execute the external task first and then the internal async {} task.

Example 5 – dispatchqueue. main Special serial main queue

As mentioned earlier, async tasks are executed in other threads. What about async tasks in the main queue? In the project. We often call DispatchQueue. Main asyncAfter (deadline:) is it in the other thread execution? If the dispatchqueue. main queue is its own queue, then even async will be executed on the main thread. Since the main queue itself is a serial queue, it can only execute one task at a time, so yes, it will process the async task after completing the current task, for example:

// It is actually executed in dispatchqueue.main.sync {}
print("1")

DispatchQueue.main.async {
    (0..<10).forEach { 
        print("async\ [$0) \(Thread.current)") 
        Thread.sleep(forTimeInterval: 0.2)}}print("3")

/** 1 3 async0 
      
       {number = 1, name = main} async1 
       
        {number = 1, name = main} async2 
        
         {number = 1, name = main} async3 ... 9 * /
        
       
      
Copy the code

Although async does not block the execution of the current thread, dispatchqueue. main can only execute the current task first and then proceed to the next task (async).

What if dispatchqueue.main.sync {} is called on the main thread? The answer is: deadlock. Sync {}. If you call dispatchqueue.main. sync {}, the result will be a deadlock.

It is also important to note that the main thread and UI operations must be executed on other threads, such as:

queue.async {	// Concurrent queue
    customView.backgroundColor = UIColor.blue
}
Copy the code

You will probably receive a Main Thread Checker: UI API called on a background Thread: -[UIView setBackgroundColor:] crash report, so the main thread is also called UI thread.

Example 6 – Performing synchronization on a concurrent queue (sync) tasks

let queue = DispatchQueue(label: "serial.com", attributes: .concurrent)

queue.sync {
    (0..<10).forEach {
        print("task-1 \ [$0): \(Thread.current)")
        Thread.sleep(forTimeInterval: 0.2)}}print("main-1")

queue.sync {
    (0..<10).forEach {
        print("task-2 \ [$0): \(Thread.current)")
        Thread.sleep(forTimeInterval: 0.2)}}print("main-2")

/** task-1 0: 
      
       {number = 1, name = main} task-1 1: 
       
        {number = 1, name = main} task-1 2: 
        
         {number = 1, name = main} task-1 3: 
         
          {number = 1, name = main} task-1 4: 
          
           {number = 1, name = main} main-1 task-2 0: 
           
            {number = 1, name = main} task-2 1: 
            
             {number = 1, name = main} task-2 2: 
             
              {number = 1, name = main} task-2 3: 
              
               {number = 1, name = main} task-2 4: 
               
                {number = 1, name = main} main-2 */
               
              
             
            
           
          
         
        
       
      
Copy the code

Using a concurrent queue to perform a synchronous task is no different from performing an operation on the main thread, because sync holds the current thread firmly in place, forcing the thread to wait for it to complete before continuing to perform other operations. Main-1 and main-2 wait until sync finishes, respectively.

Example 7 – Performing asynchrony on a concurrent queue (async) tasks

The queue starts to execute tasks concurrently only when the thread is about to perform async on a queue. It is not possible for the thread to start tasks across the currently performing operation. Here’s an example:

// Specify to create a concurrent queue (.concurrent)
let queue = DispatchQueue(label: "concurrent.com", attributes: .concurrent)

(0..<100).forEach {
    print("main-\ [$0)")
    Thread.sleep(forTimeInterval: 0.02)
}

queue.async { print("task-1".Thread.current) }
queue.async { print("task-2".Thread.current) }
queue.async { print("task-3".Thread.current) }
queue.async { print("task-4".Thread.current) }
queue.async { print("task-5".Thread.current) }
queue.async { print("task-6".Thread.current) }

print("main-end")

/** main-0 main-1 main-2 ... 99 task-2 
      
       {number = 3, name = (null)} task-4 
       
        {number = 3, name = (null)} task-5 
        
         {number = 3, name = (null)} task-3 
         
          {number = 5, name = (null)} task-6 
          
           {number = 3, name = (null)} print("main-end") task-1 
           
            {number = 4, name = (null)} */
           
          
         
        
       
      
Copy the code

Since the main thread is also a serial queue, the program will be executed sequentially, and queue. Async will not be executed until all loops are completed. Since it is a concurrent queue, all tasks will be executed at the same time, the execution order is not fixed, and the last main-end may be inserted in the queue before and after a task is completed.

Because the task is already queued out before main-end is executed. For the main thread, the time it takes to finish printing main-end is fixed, but the execution of concurrent tasks in the queue is not fixed (executing tasks takes time). The main thread does not wait for all async tasks to finish before continuing to print main-end.

Therefore, if the main thread needs to print main-end at a certain time when async is executed, main-end will be printed and unfinished tasks in async will continue to be executed, as shown in the figure below:

As you can see, the queue does not start to execute tasks concurrently until the end of the loop. The main-end print is executed after queue.async, but because the queue takes time to execute the task, it is possible to execute main-end before queue.async completes.

For a thread, all operations are executed in a fixed order. There is no such thing as a thread performing multiple tasks at the same time. Concurrency means that one thread is assigned to each task, and when one thread is finished, it is reused to execute other tasks that have not yet been executed in the queue.

One thread is only responsible for performing all operations in its current task, and when other threads are started (provided that the same thread is not started), they execute tasks independently on their own threads. Here’s an example:

Let’s say you’re going to run 100 meters, and at the 50 meter mark, five people are going to be running with you, and at the end of the race, you might be running faster than all of them, or any of them might be running faster than you.

Then you can imagine that the “five people” are concurrent tasks (simultaneous execution) and the “you” is the current thread.

Example 8 – Concurrent queue confusion –sync { sync {} }

So when does that same thread start? In other words, if there is a thread 3 executing, another thread 3 task will be started before the thread 3 has finished executing. This is almost impossible for async tasks (and I say almost impossible because I’m not sure, at my best guess), which means that in order to start the same thread for an asynchronous task, you have to wait until the previous thread has finished and then use that thread for another task.

But for sync tasks, I can start another sync {} task inside sync {} while sync is still running, because sync {} is destined to be executed on the main thread. In this way, while one thread is still executing, another thread will start executing the task. In serial queues, we already know that this can cause a deadlock, but what about concurrent queues? Such as:

let queue = DispatchQueue(label: "concurrent.com", attributes: .concurrent)
queue.sync {
    print("sync-start")
    queue.sync {
        (0..<5).forEach {
            print("task \ [$0): \(Thread.current)")
            Thread.sleep(forTimeInterval: 0.5)}}print("sync-end")}/** sync-start task 0: 
      
       {number = 1, name = main} task 1: 
       
        {number = 1, name = main} task 2: 
        
         {number = 1, name = main} task 3: 
         
          {number = 1, name = main} task 4: 
          
           {number = 1, name = main} sync-end */
          
         
        
       
      
Copy the code

We have seen the result that tasks are executed sequentially, and the internal sync blocks the external sync. We also know that the question is why the internal sync can execute when the external sync {} has not finished executing.

The most important thing to understand first is why sync {} inside a serial queue doesn’t work. The most important reason is that a serial queue can only perform one task at a time, so it cannot perform the next task (internal sync) until its previous task (external sync) has completed.

Concurrent queues, on the other hand, can perform multiple tasks simultaneously. That is, the internal sync is ready to execute without waiting for the external sync to complete. However, because it is a synchronization task, the system waits for the internal sync to complete and the external sync to continue.

Note that this execution is not inconsistent with the above statement that there is no single thread executing multiple tasks at the same time. This is because the external thread stops when the internal sync is performed, and if the internal sync is performed at the same time as the external sync is performed, it is considered simultaneous.

Because sync is on a single thread (the main thread), when you specify sync as the task, the main thread knows that it is going to perform the sync task and wait until the sync is complete before performing any other operations. For example, you can think of Sync as a method:

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

queue.sync {
    print("sync-start")
	queueSync()
    print("sync-end")}Queue.sync {}
func queueSync(a){(0..<5).forEach {
        print("task \ [$0): \(Thread.current)")
        Thread.sleep(forTimeInterval: 0.5)}}Copy the code

About First in, First Out (FIFO)

First in, first out (FIFO) means first in, first out (FIFO). When we want to execute some tasks, these tasks are stored in its queue. When the thread enters the task code block, it must finish the task first, and then the task is out of the queue. After the task is out of the queue, the thread can continue to execute the next task.

The same is true for concurrent queues. When different threads enter a block of code for tasks at the same time, they must first complete the tasks and then unqueue them before they can continue to perform other tasks.

Example 9 – Number of concurrent and thread performance

let queue = DispatchQueue(label: "concurrent.com", attributes: .concurrent)
(0..<100).forEach { i in
    queue.async { print("\(i) \(Thread.current)")}}Copy the code

What happens? The answer is no, it just opens up a lot of threads to perform these asynchronous tasks. As mentioned above, each asynchronous task is executed on a different thread. So if many asynchronous tasks are executed simultaneously, as we have here, 100 asynchronous tasks are started at the same time, does the system have 100 threads to execute each asynchronous task? It’s not impossible, depending on your CPU, if the maximum number of threads the system can handle is 10 while your App is running, then those 10 threads will be opened up to repeat tasks, 10 asynchronous tasks at a time.

If the number of threads is capped, the remaining tasks cannot be executed until the threads of the previous asynchronous tasks have finished executing.

In a word, it is reuse. The first thread is used to execute the task that has not yet been executed. If the number of threads opened exceeds the limit, the later task will wait for the previous thread to complete.

But if we open up a lot of threads, does that have a negative impact on our application? The answer is yes, starting a thread consumes a certain amount of memory space and system resources, if there are many threads at the same time, it will leave the application with very little memory, the application will run very slow, so it is not the more threads the better, the developer needs to balance.

Example 10 -dispatchqueue.global (_:) global concurrent queue

In addition to the serial main queue, the system also creates a global concurrent queue (dispatchqueue.global ()) for us. If we don’t want to create our own concurrent queue, we use the system’s (which we usually do).

DispatchQueue.global().async {
    print("global async start \(Thread.current)")
    DispatchQueue.global().sync {
        (0..<5).forEach {
            print("roop\ [$0) \(Thread.current)")
            Thread.sleep(forTimeInterval: 0.2)}}print("global async end \(Thread.current)")}/** global async start 
      
       {number = 3, name = (null)} roop0 
       
        {number = 3, name = (null)} roop1 
        
         {number = 3, name = (null)} roop2 
         
          {number = 3, name = (null)} roop3 
          
           {number = 3, name = (null)} roop4 
           
            {number = 3, name = (null)} global async end 
            
             {number = 3, name = (null)} */
            
           
          
         
        
       
      
Copy the code

Just like the main queue, it is special in that even with sync, the task will be executed on another thread. As for which thread it will be executed, I guess it must be executed by the thread that is executing the external async, because sync will suspend the thread for subsequent operations. Wait until sync is finished and then execute, that is, in this case it can only be executed sequentially, so it seems that only one thread is sufficient and there is no need to start a new thread to execute the internal sync.

In addition, there is only one global concurrent queue, instead of creating one at a time by calling the system, they are equal after testing:

let queue1 = DispatchQueue.global()
let queue2 = DispatchQueue.global()

if queue1 == queue2 { print("Equal")}// Prints are "equal".
Copy the code

conclusion

In the previous example, the relevant concepts are extended to follow the example, speaking is not so unified, here is a summary.

  • The queue

    • Serial queues When tasks are executed in a serial queue, they are executed in a fixed order and can only execute one task before proceeding to the next (meaning that serial queues can only execute one task at a time).

    • Concurrent queue

      A concurrent queue can execute multiple tasks at the same time. The tasks are not necessarily executed in sequence. The system automatically assigns the tasks to be executed first.

  • task

    • Synchronization task

      Synchronous tasks are executed on the main thread (with the exception of dispatchqueue.global ().sync), whether in serial or asynchronous queues.

      A synchronization task blocks the current thread, forcing the current thread to wait for it to complete.

      In serial queues, tasks nested with sync {} cause deadlocks.

    • Asynchronous tasks

      Asynchronous tasks are executed by other threads (dispatchqueue.main.sync is an exception). The serial queue executes asynchronous tasks with only one thread, while the concurrent queue executes asynchronous tasks with multiple threads.

      Asynchronous tasks do not block the current thread, and the thread can continue to perform other tasks/operations without waiting for the asynchronous task to complete.

      Asynchronous tasks do not cause deadlocks.