【问题标题】:Sensible strategy for unit testing expected and non-expected deadlock behavior单元测试预期和非预期死锁行为的明智策略
【发布时间】:2011-09-08 13:33:21
【问题描述】:

我想要一些关于如何测试一些可以阻塞的对象的想法,等待其他参与者。要测试的特定单元是参与者之间的通道,参与者本身是用于测试目的的模拟夹具。

验证参与者是否在预期发生死锁时会很好,但这对我来说并不是非常重要,因为可以合理地将死锁之后描述为未定义。

更关键的是验证参与者定义的交互是否没有死锁。

无论哪种情况,我都不确定最佳测试策略应该是什么。我目前的想法是让测试运行程序为每个参与者启动一个线程,休眠一段时间,然后发现子线程是否已经返回。如果它们没有及时返回,则假设它们已经死锁,并安全地终止线程,并且测试失败(如果预期死锁,则测试成功)。

这感觉有点概率,因为可能有各种各样的原因(尽管不太可能)一个线程可能需要比预期更长的时间才能完成。还有其他解决这个问题的好方法吗?

编辑:我确信测试的健全性会很好,但我认为我不需要它。我正在考虑三个级别的测试确定性。

  • “实际行为已证明与预期行为相匹配”死锁不会发生
  • “实际行为与预期行为匹配”死锁在 N 次测试中未发生
  • “实际行为与预期行为一致”N 次测试在预期期限内完成

第一个当然是通过有价值的测试,但 ShiDoiSi 的回答说明了它的不切实际。第二个明显弱于第一个,但仍然很难;您如何确定进程网络实际上已陷入僵局?我不确定这比第一个更容易证明(可能更难)

最后一个更像我的想法。

【问题讨论】:

  • Growing Object-Oriented Software Guided by Tests 这本书有一些关于如何在多线程代码上运行测试的技巧。对不起,我不能分享更深刻的东西,但我从来没有测试过什么东西可以优雅地对僵局做出反应。我只创建了测试以确保没有死锁。
  • 测试无死锁与测试死锁相同;即使发生死锁,测试仍必须完成(除非您愿意 kill 一个测试失败并死锁的测试运行器。)
  • 这个问题让我想起了无限饼干机:davedoyle.com/prof/pastProjects/Nerd/humor/csci_sesame.html

标签: unit-testing language-agnostic deadlock verification


【解决方案1】:

为了测试是否没有死锁,您可以使用 NUnit 的 TimeoutAttribute 等效项,如果执行时间超过上限,它会中止并失败测试。您可以*提出一个好的超时值,例如,如果测试未在 30 秒内完成 - 出了点问题。

我不确定(或者我没有遇到过这种情况)断言发生了死锁。死锁通常是不可取的。我不知道如何编写一个失败的单元测试,除非测试阻塞 - 单元测试通常应该是快速且非阻塞的。

【讨论】:

  • 测试实际死锁对我来说不如测试它是否不存在那么重要。我使用的测试框架(python 的 unittest)看起来没有这个原语,但添加它很简单。
【解决方案2】:

学术界可能会告诉你(事实上它现在就在告诉你;)你应该对一些所谓的模型检查框架(CSP, pi-calculus)进行忠实的抽象。然后,这将模拟抽象执行(通过所有可能的调度程序交错进行详尽搜索)。当然,诀窍是确保抽象实际上是忠实的。您不再检查程序的实际源代码,而是检查其他语言的源代码。

否则,会想到针对特定语言使用Java Path Finder/Explorer(其作用非常相似)之类的强硬方法。 类似的 C 研究原型也存在,Intelother companies 也有专门的工具。

您正在研究计算机科学研究中的热门话题之一,对于非平凡/真实系统,详尽的测试和形式验证都不容易适用于真实代码。

一种有价值的方法可能是检测您的代码,以便它能够真正检测到死锁,并可能尝试恢复。对于检测死锁,FreeBSD kernel 使用一组 C 宏来跟踪锁的使用情况,report potential violations 通过witness(4) 机制。但同样,很少发生的错误也只会很少被发现。

(免责声明:我没有参与上面链接的任何商业工具 --- 我只是添加它们来让您了解您面临的问题的难度。)

【讨论】:

    【解决方案3】:

    既然您已经做了足够多的抽象来模拟参与者,为什么不进一步抽象出您的线程同步(互斥、信号量等)?

    当您考虑什么构成死锁时,您可以在测试中使用专门的、可识别死锁的线程同步器。通过“死锁感知”,我并不是说它应该通过使用超时等以蛮力方式检测死锁,而是通过标志、计数器等方式了解导致死锁的情况。它可以检测死锁,同时可选地提供预期的线程同步功能。我的基本意思是,为您的测试使用检测线程同步...

    这太抽象了,说起来容易做起来难。而且我并没有声称已经成功地做到了。我可能只是在这里很傻。但是,如果您可以只提供一个(不完整的)测试,那么问题可能会受到更具体的攻击。

    【讨论】:

      【解决方案4】:

      可靠地测试死锁的唯一方法是检测锁定子系统以检测和报告死锁。上次我必须这样做时,我们构建了它的调试版本,记录哪些线程持有哪些锁,并在每次锁定获取调用时检查潜在的死锁。在一个有很多锁定的系统中,它可能是一个重量级的操作,但我们发现它非常有价值,因此我们重新组织了子系统,以便我们可以在运行时通过开关打开和关闭它,即使在生产构建中也是如此。

      【讨论】:

      • 这实际上也是在 FreeBSD 内核中采用的方法;我已经扩展了我的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-14
      • 2015-06-12
      • 2018-01-15
      • 1970-01-01
      • 2017-12-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多