首页 > 解决方案 > 单元测试线程:如何确认线程被阻塞

问题描述

我正在为多线程系统编写一组单元测试。我想写一个测试,确认线程 B 被线程 A 阻塞。我当前的解决方案,我知道这是不正确的,是让线程 A 休眠一段时间,然后让主线程确定线程 B至少花了那么长时间才能完成。我意识到这个测试并不是真的有效,因为线程 B 可能只是被系统调度出来并且需要很长时间才能运行,这与被阻塞无关。


更多信息,基于评论:

我无法详细说明,但是 $dayjob 已经实现了它自己的任务线程系统,与例如 Android 的 AsyncTask 并没有太大的不同,我的任务是(明白吗?)为它编写单元测试。我已经编写了十几个我很满意的单元测试,并且满意是完全确定性的。但是只有几个单元测试让我望而却步。在某些情况下,线程需要等待特定条件,我需要确认它实际上是在该条件下被阻塞的,而不是恰好在错误的时间休眠。

我提出的测试不会产生任何错误的失败,但如果调度程序碰巧在错误的时间让线程进入睡眠状态,仍然可能产生错误的成功。


更多信息:我不是在测试使用线程库的代码,而是在测试库本身。

标签: c++multithreadingunit-testingsynchronous

解决方案


多线程代码的测试是一个非常有趣的领域,IMO。我会说你专注于太低级的事情 - 你试图检测“线程 B 被线程 A 阻塞”。您可能还有很多其他问题(竞争条件、数据竞争),例如没有用于存储/加载的适当内存屏障的共享变量。这就是为什么我更愿意在以下两个方面专注于您自己的程序的功能/状态:

  1. 测试期间没有达到意外状态(没有相关的错误,包括竞争条件或数据竞争)
  2. 测试按预期进行(没有意外的长时间等待/阻塞/死锁由于任何原因,包括竞争条件或数据竞争)

例如,在单生产者/单消费者队列测试的情况下,我们编写了一个有 2 个线程的测试。一个线程放置 N 个(最好是数百万个)值(例如,从 1 到 N 的数字)。另一个线程从队列中获取 N 个值。

为了确保我们没有竞争条件或数据竞争,消费者可能会检查每个值是否等于前一个值 + 1。这就是我们检查预期状态的方式。

为了确保我们取得进展,生产者可能会在每次 put() 之后增加一个 PutCounter。消费者 - 每个 take() 之后的 TakeCounter。测试完成后,我们检查我们的最终进度不变量PutCounter == TakeCounter == N。由于我们可能有一个影响进度的bug,我们应该在测试中设置一个合理的Timeout。

由于此类测试需要大量时间才能运行,因此我已在开发人员端禁用它们,并且仅在启用 CI 时禁用它们。

至于您的特定情况,我们可能会测试测试在超时期间没有取得进展,并且仅用于调查/修复错误,我们进行线程转储并看到发生这种情况是因为“线程 B 被线程 A 阻塞”。想想每个线程应该做什么?每个线程或整个系统的“进度”是什么意思?然后准备一个测试场景并通过超时来验证是否达到了最终进度。


推荐阅读