【问题标题】:dispatch_sync inside dispatch_sync causes deadlockdispatch_sync 内部 dispatch_sync 导致死锁
【发布时间】:2014-05-29 17:52:01
【问题描述】:

我刚刚在objc.io Going Fully Asynchronous 上读到了这篇文章,但找不到很好的解释

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

一旦我们到达第二个 dispatch_sync 我们就会死锁:我们不能 dispatch 到 queueA 上,因为有人(当前线程)已经在上面了 排队,永远不会离开它。

只要我明白

  1. dispatch_sync 只需添加工作项(我避免使用“块”这个词 因为它可能会混淆)到 queueA,然后这个工作项将被发送 到 queueA 的目标队列,然后 GCD 将保留一个线程 此工作项的 threadWorkItem
  2. 当我到达 (b) 时,我在线程 threadWorkItem 中(假设 threadWorkItem 是这个线程的名称),所以我认为将另一个工作项加入队列queueA 没问题。但是有人说这个时候queueA被保留了,queueA被阻塞了->导致死锁,这让我很困惑

我已经阅读了很多与此相关的帖子,例如Deadlock with dispatch_syncWhy can't we use a dispatch_sync on the current queue?Why is this dispatch_sync() call freezing?,...但找不到很好的解释。有人说dispatch_sync 阻塞了队列,有人说它阻塞了当前线程,... :(

那为什么会导致死锁呢?

【问题讨论】:

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


    【解决方案1】:

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

    但要清楚,dispatch_sync 阻塞当前线程,而不是当前队列。处理并发队列时,后续调度的block会使用不同的worker线程,不会产生死锁。

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

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

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

    【讨论】:

    • 1.因此,如果线程(用于调用第一个 dispatch_sync)和线程(GCD 将用于运行第二个 dispatch_sync 的工作项)相同,就会出现问题?这几乎不会发生,因为最终所有工作项都将进入全局队列,而 GCD 将从线程池中获取线程,这几乎不会使这相同?! 2. 即使这是相同,我认为第二个 dispatch_sync 只是将工作项发送到 queueA,此时不涉及线程?!!
    • in 2. 我的意思是那个时候,工作项只是发送到 queueA 并没有立即执行,它将在以后的运行循环中运行。所以当时没有涉及到线程
    • 在您的第二条评论“对于串行队列,dispatch_sync 将阻塞线程”这是什么线程?调用第1个dispatch_sync的线程还是第1个dispatch_sync的工作项运行所在的线程?
    • @entropy dispatch_sync 总是阻塞调用它的线程,等待分派的代码块完成。如果一个代码块已经分派到某个串行队列,它本身尝试将另一个代码块同步分派到同一个队列,那么第二个分派的代码块将永远不会开始,因为它在第一个块完成之前无法启动。但是第一个块由于调用了dispatch_sync而被阻止。
    【解决方案2】:

    使用此代码从任何线程调用主线程,而不会有死锁的风险。请记住,如果队列的层次结构很深,您仍然可能会陷入僵局。

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

    【讨论】:

      猜你喜欢
      • 2018-02-09
      • 1970-01-01
      • 1970-01-01
      • 2012-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-09
      • 1970-01-01
      相关资源
      最近更新 更多