【问题标题】:Is there a limit on the number of blocks that can be submitted to a serial queue in GCD?可以提交到 GCD 中的串行队列的块数量是否有限制?
【发布时间】:2013-08-28 05:13:47
【问题描述】:

我定义了一个使用延迟实例化返回串行调度队列的属性,如下所示:

@property (nonatomic, readonly) dispatch_queue_t queue;

- (dispatch_queue_t)queue
{
    if (!_queue) {
        _queue = dispatch_queue_create("com.example.MyQueue", NULL);
    }
    return _queue;
}

然后假设我为某个按钮定义了一个动作方法,它向队列中添加了一个块:

- (IBAction)buttonTapped:(UIButton *)sender
{
    dispatch_async(self.queue, ^{
        printf("Do some work here.\n");
    });
}

实际方法的代码比简单的打印语句更复杂,但对于示例来说就可以了。

到目前为止一切顺利。但是,如果我构建并运行程序,我可以点击按钮 10 次并看到块运行,但是当我点击第十一次时,程序会挂起。

如果我将串行队列更改为并发队列,则没有问题。我可以将任意数量的块分派到队列中。

知道会发生什么吗?可以发布到串行队列的块数是否有限制?

【问题讨论】:

    标签: queue grand-central-dispatch dispatch


    【解决方案1】:

    在回答关于最大块的问题时,我知道对可以排队的内容没有实际限制(可用内存除外)。当然,您应该可以排十个以上的队而不会发生事故。

    但是您的 queue getter 方法中有错字。您正在设置_queue,但返回queue。您应该返回您设置的相同变量。看起来您必须定义两个 ivars;也许一个是您手动定义的,一个是合成的?如果您有一个手动声明的实例变量,您应该删除它并确保您的 getter 方法使用相同的实例变量,即为您合成的那个。另外,您是否在 init 方法中初始化此 ivar?

    如果修复此问题不能解决问题,则问题可能在于您分配到此队列的特定代码,您应该与我们分享。那里有同步代码吗?与任何共享资源的任何交互?

    【讨论】:

    • 我在示例代码中将queue 更改为_queue。至于块中的实际代码,它是相当无害的。它确实执行呼叫NSURLConnectionsendSynchronousRequest。它不包含任何其他同步代码,不调用 dispatch_async 等。我发现最不寻常的是代码在 10 个和正好 10 个块被添加到队列时失败。可能是巧合,但不寻常且完全可重复。
    • @user2460554 如果您将NSURLConnection 代码替换为sleep(5)(睡眠五秒钟,足够您排队十多个请求的时间)和NSLog,我怀疑会没事的。如果这可行,您就知道问题是由于您的代码在该调度块中的结果。我可以想象(a)一些资源争用(你说你没有做任何同步,这在多线程代码中通常很重要);或 (b) 您的服务器在一段时间内拒绝太多请求的问题(例如 DDNS 保护)。
    • 首先,感谢您的回复...我当然可以尝试您的建议(事实上,我会尝试)。但是,这并没有向我解释为什么我在使用串行队列而不是并发队列时遇到问题。使用并发队列,一切正常。你能想出为什么并发队列可以工作但串行队列不行的原因吗?
    • @user2460554 不,我很难猜测为什么并发成功,但串行失败(坦率地说,通常情况相反),此外,为什么它总是在十个请求后失败,而不是九个,从来没有十一个,等等。我肯定排了十多个,没有发生任何事故,所以我不怀疑有任何 GCD 限制。如果您进行一些琐碎的测试,我敢打赌您会发现问题取决于排队调度请求中的代码,而不是 GCD 本身固有的任何东西。
    【解决方案2】:

    好的,我终于解决了这个问题。

    首先,当我报告并发队列工作正常,但串行队列没有,我错了。两种类型的队列都失败了。当我观察到一切正常时,那实际上是在使用主队列。所以,在那种情况下,真的没有任何并发​​性。

    也就是说,这个问题是一个死锁问题,与我在辅助线程上处理时通过主线程记录信息有关——来自串行或并发队列。更糟糕的是,我的应用程序使用 Lumberjack 进行日志记录,这引入了额外的线程问题。为了解决这个问题,我将每个对 Lumberjack 日志记录方法的调用都包装如下:

    dispatch_async(dispatch_get_main_queue(), ^{
        // do logging here
    });
    

    这解决了问题。感谢cmets。他们最终让我找到了解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-11
      • 1970-01-01
      • 2012-01-01
      • 1970-01-01
      • 2015-12-04
      • 2011-12-04
      • 2012-12-17
      • 2022-08-23
      相关资源
      最近更新 更多