调试间歇性卡住的NSOperationQueue

时间:2011-11-22 17:03:00

标签: objective-c ios debugging nsoperation nsoperationqueue

我有一个非常讨厌的iOS应用程序:我的NSOperationQueue中的操作由于某种原因挂起并且没有完成执行,因此其他附加操作正在排队但仍未执行。这反过来导致应用程序无法开始执行关键功能。我还没有找到任何模式,除了它每周左右发生在我的一个同事设备上。此时从Xcode运行应用程序没有帮助,因为杀死和重新启动应用程序暂时解决了这个问题。我已经尝试将调试器附加到正在运行的进程中,我似乎能够看到日志数据,但我添加的任何断点都没有注册。我添加了一个NSLogs的面包屑痕迹,试图找出它挂在哪里,但这还没有导致解决方案。

我最初描述了another question中的错误,由于我无法提供有关此问题的信息,因此我猜测的答案尚未明确。

一位朋友曾告诉我,可以在某个特定时刻以某种形式保存应用程序的整个内存堆栈,并将该确切的内存状态重新加载到不同设备上的进程中。有谁知道我怎么能做到这一点?如果有可能在下次有人遇到该错误时我可以保存那个确切的记忆状态并复制以测试我所有可能解决方案的理论。或者有没有不同的方法来解决这个问题?作为一项临时措施,您是否认为当应用程序进入此状态时强行使应用程序崩溃是有意义的,这样实际用户就会不那么困惑了?我对此有不同的看法,但无论如何用户都必须从多任务基座中杀死应用程序才能再次使用该应用程序。我可以检查操作队列计数或为此创建某种超时代码,直到我真正指出这个错误。

5 个答案:

答案 0 :(得分:3)

这听起来像一个非常罕见的竞争条件的僵局。您还提到使用maxConcurrentOperationCount为2.这意味着:

  1. 某些操作正在阻塞操作队列,并且为了释放一些锁而主要等待操作完成
  2. 两个操作正在等待释放一些锁
  3. 1似乎不太可能,因为队列应该允许完全阻止2个并发操作,除非你使用一些具有concurency问题的系统函数并阻止你排队而不是只有一个线程。

    我在这种情况下首次尝试调试是连接调试器并暂停执行。之后,您可以查看所有线程的堆栈跟踪。您应该能够找到由操作队列生成的2个线程,之后我将查看负责的函数以查找可能在某个锁上等待的代码。请务必考虑系统功能。

答案 1 :(得分:1)

很难解决不会崩溃应用程序而只是挂起一个线程的错误。如果您通过查看代码逐步检查是否存在任何可能的死锁或竞争条件而无法找到错误,我建议您实施一些日志记录。

每次添加时都将日志写入磁盘。这不是最有效的内存方式,但是如果你给你的同事启用了日志记录,你可以在出现问题时从他的iPhone中提取日志。即使应用程序仍在运行。

确保记录您采取的每个步骤,包括您怀疑打破App的代码周围的重要变量值。通过这种方式,您可以看到应用程序正在执行的操作以及应用程序的状态。

希望这会有所帮助。我现在不想恢复应用程序的内存状态,所以无法帮助你。

请注意;如果应用程序崩溃了,你可以使用其他一些工具,但如果我说得对,那不是这里的情况吗?


我读了描述错误的问题,我会尝试登录磁盘当前正在运行的操作正在做什么。似乎操作会偶尔挂起,并且有一个错误。如果您可以在运行操作时记录调用的方法,这将显示哪个函数调用将挂起应用程序,您可以开始查看。

答案 2 :(得分:1)

在这里检查假设,因为这永远不会伤害:你真的有证据表明你的背景线程是悬挂的吗?根据您的报告,观察到的行为是您在后台线程中放置的任务未达到预期的结果。这并不一定表明线程已挂起 - 它可能只是表明特定条件意味着线程因所有任务完成而关闭,而没有任务实现您想要的任务。

添加:在评论中给出您的答案,在我看来,下一步是在项目开始在队列中执行时使用日志记录,以便您可以识别它是哪些项目导致队列被阻止。最好的猜测是,如果它们都属于某个类别,则它是某类物品或某些特征。记录足够的日志作为执行每个项目的第一步,您将对项目进行合理的表征,然后一旦您获得进入此状态的真实设备,请检查日志并查看导致此问题的条件。这应该使您能够在调试期间或在模拟器中可靠地重现设备上的问题,然后进行修整。

换句话说 - 我会首先关注识别有问题的操作,而不是试图找出事情停滞的特定代码行。

答案 3 :(得分:1)

你没有这么说,但我认为当人类操作员使用应用程序时会发生错误?也许您应该为此应用程序添加一个自动模式,其中应用程序模拟用户通常执行的相同操作,使用随机时间来启动不同的操作。然后,您可以让应用程序在所有设备上无人值守地运行,并增加查看问题的机会。

此外,由于问题似乎与NSOperationQueue有关,因此您可以将其子类化,以便可以将日志记录添加到更有趣的方法中。例如,每次添加操作时,您都应该记录队列的状态,因为您怀疑它有时会被暂停。

另外,我在你的另一个问题上提出了这个建议,你可能想设置一个观察者,以便在队列进入暂停状态时收到通知。

祝你好运。

答案 4 :(得分:0)

就我而言

  

启动

而不是

  

必须被覆盖。

如有疑问,请参阅https://developer.apple.com/documentation/foundation/nsoperation#1661262?language=objc与您的实施不一致