为什么为什么并发Queue.sync不会导致死锁

时间:2018-11-14 05:56:16

标签: ios concurrency grand-central-dispatch deadlock

此代码将死锁,因为:

  1. 它们在同一线程中
  2. print(2)必须等待print(3)
  3. print(3)必须等待print(2)

例如:

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

为什么concurrentQueue中不会导致死锁?它们也处于同一线程中。

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

1 个答案:

答案 0 :(得分:0)

您问:

  

为什么concurrentQueue中不会导致死锁?它们也处于同一线程中。

否,它们必须在同一线程中。它们位于相同的队列中,但不一定是相同的 thread。(作为优化的一部分,请参见下文,它实际上可能最终在同一线程上运行,但不一定如此。但是从逻辑上讲,您应该将其视为在单独的线程上运行。)

这是“并发队列”背后的全部思想,即它们可以在不同的线程上运行单独的调度任务。这就是它们允许并发操作的方式。并发队列上的一个调度任务可以在一个线程上运行,而同一队列上的另一调度任务可以在单独的线程上运行。

就像旧的Concurrency Programming Guide在其“并发队列”的定义中所说的:

  

当前正在执行的任务在分派队列管理的不同线程上运行。

或者,作为DispatchQueue documentation says

  

DispatchQueue管理工作项的执行。提交到队列的每个工作项都在系统管理的线程池中进行处理。 [强调]


这让您更加困惑的是,如果您是dispatching synchronously ...

,则可以进行GCD优化。
  

作为性能优化,此函数在可能的情况下在当前线程上执行块...

因此,当从队列中同步调度时,实际上 最终可以在同一线程上运行该代码(除非您是从后台队列向主队列调度)。考虑:

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)
}

第二条print语句将显示,即使您有一个完全不同的队列,作为上述sync优化的一部分,它也可以在当前线程上运行代码。如果内部sync调用是与派发外部块的那个queue相同的,则同样如此。

现在,当查看优化结果时,感觉就像是串行队列方案,但事实并非如此。串行队列一次只允许运行一个调度的任务,因此,按照定义,同步调度(阻塞当前线程,直到调度的块运行)的尝试本身就是一个死锁。

但是,尽管由于进行了sync优化,并发队列本身并没有死锁,但最终可能会在同一线程上运行代码。