dispatch_sync中的dispatch_sync导致死锁

时间:2014-05-29 17:52:02

标签: ios sync grand-central-dispatch deadlock dispatch

我刚刚在objc.io Going Fully Asynchronous上看过这个,但找不到好的解释

dispatch_queue_t queueA; // assume we have this
dispatch_sync(queueA, ^(){  // (a)
    dispatch_sync(queueA, ^(){ // (b)
        foo();
    });
});
  

一旦我们点击第二个dispatch_sync,我们就会陷入僵局:我们无法发送   在queueA上,因为有人(当前线程)已经在那上面了   排队,永远不会离开它。

只要我明白

  1. dispatch_sync只需添加工作项(我避免使用“块”一词 因为它可能会混淆)到队列A,然后将发送此工作项 到queueA的目标队列,然后GCD将保留一个线程 此工作项的 threadWorkItem
  2. 当我到达(b)时,我在线程 threadWorkItem (假设 threadWorkItem 是此线程的名称),所以我认为将另一个工作项排入queueA没问题。但有些人说,此时,queueA被保留,queueA被阻止 - >导致死锁,这让我感到困惑
  3. 我已经阅读了许多与此相关的帖子,例如Deadlock with dispatch_syncWhy can't we use a dispatch_sync on the current queue?Why is this dispatch_sync() call freezing?,但是找不到好的解释。有人说dispatch_sync阻塞队列,有人说它会阻塞当前线程,...... :(

    那为什么会造成僵局?

2 个答案:

答案 0 :(得分:11)

dispatch_sync阻塞当前线程,直到调度的代码完成,如果您从串行队列同步调度,那么您也有效地阻塞了队列。因此,如果您从串行队列同步调度到自身,则会导致死锁。

但要明确,dispatch_sync会阻止当前线程,而不是当前队列。处理并发队列时,不同的工作线程将用于随后调度的块,并且不会产生死锁。

您似乎正在回应并发编程指南 Dispatch Queues章末尾的讨论,其中说:

  

不要从传递给函数调用的同一队列上执行的任务中调用dispatch_sync函数。这样做会使队列死锁。如果需要调度到当前队列,请使用dispatch_async函数异步执行此操作。

这不完全正确。如果(a)您使用并发队列执行此操作; (b)有可用的工作线程,这不会导致死锁。但这是一种不好的做法,应该避免使用。

答案 1 :(得分:0)

使用此代码从任何线程到主线程进行调用,没有死锁风险。请记住,如果队列的层次结构很深,你仍然可以陷入僵局。

static inline void dispatch_synchronized (dispatch_queue_t queue,
                                          dispatch_block_t block)
{
    dispatch_queue_set_specific (queue, (__bridge const void *)(queue), (void *)1, NULL);
    if (dispatch_get_specific ((__bridge const void *)(queue)))
        block ();
    else
        dispatch_sync (queue, block);
}