Home > database >  There is no thread deadlock in this serial dispatch queue, why?
There is no thread deadlock in this serial dispatch queue, why?

Time:08-04

Here is the code:

let serialQueue = DispatchQueue(label: "com.hello")
print("current:",Thread.current)
for i in 0...9 {
    serialQueue.sync {
        print(i,Thread.current)
    }
}
print("hello world")

--------------------
output as below:
current: <NSThread: 0x7f8dd5c0dfe0>{number = 1, name = main}
0 <NSThread: 0x7f8dd5c0dfe0>{number = 1, name = main}
1 <NSThread: 0x7f8dd5c0dfe0>{number = 1, name = main}
2 <NSThread: 0x7f8dd5c0dfe0>{number = 1, name = main}
3 <NSThread: 0x7f8dd5c0dfe0>{number = 1, name = main}
4 <NSThread: 0x7f8dd5c0dfe0>{number = 1, name = main}
5 <NSThread: 0x7f8dd5c0dfe0>{number = 1, name = main}
6 <NSThread: 0x7f8dd5c0dfe0>{number = 1, name = main}
7 <NSThread: 0x7f8dd5c0dfe0>{number = 1, name = main}
8 <NSThread: 0x7f8dd5c0dfe0>{number = 1, name = main}
9 <NSThread: 0x7f8dd5c0dfe0>{number = 1, name = main}
hello world

When I write down this code,I think there should be NO deadlock,and it works as expected.But once I saw the output, current thread and the thread which GCD used are the same thread! Why there is no thread deadlock when current thread and the thread which GCD used are the same thread? I thought a custom serial queue should not pick up main thread to run it's task, but it does, why?

Once I changed the serialQueue to DispatchQueue.main, thread deadlock does happen,this is what I expected.

let serialQueue = DispatchQueue.main
print("current:",Thread.current)
for i in 0...9 {
    serialQueue.sync {
        print(i,Thread.current)
    }
}
print("hello world")

CodePudding user response:

You need to understand the difference between threads and queues. With GCD you dispatch onto a queue. GCD then looks for an available thread to perform the work.

The main queue is a special queue that will only perform work using the main thread. Other queues can use any available thread, including the main thread.

In your first code block you are dispatching synchronously onto a serial queue from the main queue. This has the effect of blocking the main queue. Since the main thread is no longer required to perform work on the main queue, GCD can use it to perform your work on the serial queue. This is faster and more efficient than dispatching the work onto some other thread.

Once the task is complete on the serial queue, the main queue is unblocked and the next iteration of the loop executes and the process repeats.

In your second code block you are trying to dispatch synchronously from the main queue onto the main queue.

This is what happens:

  • You dispatch a synchronous task, which blocks the main queue as it did before.
  • This time the main queue is required to execute the task, but the main queue is blocked until the dispatched task completes
  • You have deadlocked!

In summary, think about queues not threads.

A small Computer Science refresher:

For a deadlock to occur two conditions must be satisfied; "Mutual exclusion" and "hold and wait". In your second code block you have both of these:

  • Mutual exclusion: only one task can be running on the main queue at a time
  • Hold and wait: The main queue is "held" by the dispatch.sync so your dispatched block cannot get the resource it needs to execute.
  • Related