Search code examples
iosswiftmultithreadinggrand-central-dispatch

DispatchQueue.sync { } blocks "thread" or "queue"


I'm quite confused.

Code below will cause a deadlock for sure:

// Will execute
DispatchQueue.main.async { // Block 1 
  // Will execute
    DispatchQueue.main.sync { // Block 2
     // Will not be executed
    }
    // Will not be executed
}

Because

  1. After we dispatch_async on main queue, it's submits the first block to the main queue to execute
  2. At some moment, the system decides to run block1
  3. The .sync method blocks "thread / queue?" <- My question
  4. Because the "thread / queue" is blocked, block 2 can't execute before block 1 finishes, because Main queue is a serial queue, it execute task serially, one can't execute before another finishes

My question is: Does sync block current thread it's executing on or current queue? (I understand the difference between thread & queue)

Most answers on the internet says it blocks thread

  1. If block thread -> How come the sync { } block can still execute since the thread is blocked?

  2. If block queue -> Make more sense? Since the queue is blocked, we can't execute one before other finishes

I found some discussions about this:

dispatch_sync inside dispatch_sync causes deadlock

Difference Between DispatchQueue.sync vs DispatchQueue.async

What happens if dispatch on same queue?


Solution

  • You asked:

    My question is: Does sync block current thread it's executing on or current queue?

    It blocks the current thread.

    When dealing with a serial queue (such as the main queue), if that queue is running something whose thread is blocked, that prevents anything else from running on that queue until the queue is free again. A serial queue can only use one thread at a time. Thus, dispatching synchronously from any serial queue to itself will result in a deadlock.

    But, sync does not technically block the queue. It blocks the current thread. Notably, when dealing with a concurrent queue (such as a global queue or a custom concurrent queue), that queue can avail itself of multiple worker threads at the same time. So just because one worker thread is blocked, it will not prevent the concurrent queue from running another dispatched item on another, unblocked, worker thread. Thus, dispatching synchronously from a concurrent queue to itself will not generally deadlock (as long as you don’t exhaust the very limited worker thread pool).


    E.g.

    let serialQueue = DispatchQueue(label: "serial")
    
    serialQueue.async {
        serialQueue.sync {
            // will never get here; deadlock
            print("never get here")
        }
        // will never get here either, because of the above deadlock
    }
    
    let concurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)
    
    concurrentQueue.async {
        concurrentQueue.sync {
            // will get here as long as you don't exhaust the 64 worker threads in the relevant QoS thread pool
            print("ok")
        }
        // will get here
    }
    

    You asked:

    1. If block thread -> How come the sync { } block can still execute since the thread is blocked?

    As you have pointed out in your own code snippet, the sync block does not execute (in the serial queue scenario).