CashGBL
CashGBL

Reputation: 57

why concurrentQueue.sync DON'T cause deadlock

This code will deadlock because:

  1. they are in same thread
  2. print(2) has to wait print(3)
  3. print(3) has to wait print(2)

For example:

DispatchQueue.main.async {
    print(Thread.current)
    DispatchQueue.main.sync {
        print(Thread.current)
        print(2)
    }
    print(3)
}

Why in the concurrentQueue won't cause deadlock? They are also in same thread.

DispatchQueue.global().async {
    print(Thread.current)
    DispatchQueue.global().sync {
        print(Thread.current)
        print(2)
    }
    print(3)
}

Upvotes: 1

Views: 1001

Answers (1)

Rob
Rob

Reputation: 437632

You ask:

Why in the concurrentQueue won't cause deadlock? They are also in same thread.

No, they're necessarily in the same thread. They're in the same queue, but not necessarily the same thread. (As part of an optimization, see below, it actually might end up running on the same thread, but not necessarily so. But logically, you should think of it as running on a separate thread.)

This is the whole idea behind “concurrent queues” is that they can run individual dispatched tasks on different threads. That’s how they permit concurrent operation. One dispatched task on a concurrent queue can run on one thread while another dispatched task on that same queue can run on a separate thread.

As the old Concurrency Programming Guide says in its definition of a “concurrent queue”:

The currently executing tasks run on distinct threads that are managed by the dispatch queue.

Or, as the DispatchQueue documentation says:

DispatchQueue manages the execution of work items. Each work item submitted to a queue is processed on a pool of threads managed by the system. [emphasis added]


What makes this a little more confusing is that there is a GCD optimization that, if you are dispatching synchronously ...

As a performance optimization, this function executes blocks on the current thread whenever possible...

So, when dispatching synchronously from a queue, it actually can end up running that code on the same thread (unless you’re dispatching from a background queue to the main queue). Consider:

let queue = DispatchQueue.global()
let queue2 = DispatchQueue(label: "queue2")

queue.async {
    print(1, Thread.current)
    queue2.sync {
        print(2, Thread.current)
    }
    print(3, Thread.current)
}

That second print statement will show that even though you have a completely different queue, it may, as part of the aforementioned sync optimization, run the code on the current thread. The same it true if that inner sync call was to the same queue to which the outer block was dispatched.

Now, when looking at the result of this optimization, it feels like this is just like the serial queue scenario, but it’s not. A serial queue only allows one dispatched task to run at a time, and thus the attempt to dispatch synchronously (blocking the current thread until the dispatched block runs) to itself is, by definition, a deadlock.

But a concurrent queue dispatching to itself will generally not deadlock. The one caveat is that the number of worker threads is fairly limited, so if you have “thread explosion” (where you might have more than 64 worker threads going), then it can deadlock, so make sure your limit your concurrency to some reasonable value.

Upvotes: 7

Related Questions