如何在condition_variable :: wait周围对包装器进行单元测试?

时间:2016-12-13 21:47:14

标签: c++ multithreading c++11 c++17

我有一个具有wait函数的类,它等待队列被填满。一旦队列非空,则通知condition_variable,等待功能清空队列并处理其元素。

我想在单元测试中验证此wrapper::wait调用(检查队列是否为空)实际上阻塞了线程。我有一个atomic_bool的片状设置,它在等待调用之后设置,并且天真地认为执行以下序列会起作用。

  1. 使用调用wait的函数创建线程,然后设置atomic_bool
  2. 创建线程后,验证atomic_bool是否具有开始值
  3. 在主线程中将某些东西推入队列,从另一个线程中更改atomic_bool
  4. 验证主线程中更改的值。
  5. 有两个缺失的同步点:两个"验证"不能保证在我希望它们发生时:

    • 我无法验证是否在线程中调用了等待。
    • 我无法验证该帖子是否有机会更改atomic_bool

    有没有办法解决此问题,而无需添加一个或多个额外的condition_variables / mutexes来同步操作?

2 个答案:

答案 0 :(得分:0)

测试这种情况的一种方法是在等待之前和之后添加一些跟踪或日志行。最后,您将比较跟踪或跟踪文件以及一些参考,以查看是否按预期发生了这些活动。

答案 1 :(得分:0)

这确实是使用std::condition_variable的完美方案,但无论如何:您可以使用多个同步点和std::atomic<int> gate。各种值将编码测试的不同阶段。等待超过某个恒定时间 T 的值将被视为测试用例失败 1

  • gate == 0:未初始化辅助线程。
  • gate == 1:初始化了辅助线程,但它还没有响应。
  • gate == 2:初始化并运行的辅助线程,可以假定已调用wait
  • gate == 3:主线程已向队列添加了一些内容,辅助线程仍可能正在等待。
  • gate == 4:辅助线程退出wait并设置最后一个值。

如果您愿意,可以省略几个同步点。

主线程

  1. gate设为0。
  2. Spawn辅助线程。
  3. gate设为1。
  4. 等待 T gate成为2;失败时:&#34;辅助线程从未到达wait&#34;
  5. 向队列中添加内容,将gate设置为3。
  6. 等待 T gate变为4;失败时:&#34;辅助线程从未退出wait&#34;
  7. 等待 T 以使辅助线程加入;失败时:&#34;在退出&#34; 之前挂起辅助线程。
  8. 辅助线程

    1. 等待 T gate变为1;失败&#34;产卵后主线程挂起&#34;
    2. gate设为2.致电wait
    3. (呼叫wait退出;队列已处理完毕。)
    4. 等待 T gate成为3;失败&#34;回到过去&#34;
    5. gate设为4并退出。
    6. 实施&#34;等待和检查&#34;部分您可以使用compare_exchangestd::chrono。可能的实现可能是这样的:

      #include <atomic>
      #include <chrono>
      #include <condition_variable>
      
      std::cv_status wait_till_reaches(
          std::atomic<int> &gate,
          std::chrono::high_resolution_clock::duration const &rel_time,
          const int desired_value,
          bool increment = false
      ) {
          const int next_value = desired_value + (increment ? 1 : 0);
          int dummy = desired_value;
          const std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
          while (not gate.compare_exchange_weak(dummy, next_value)) {
              dummy = desired_value;
              if ((std::chrono::high_resolution_clock::now() - start) >= rel_time) {
                  return std::cv_status::timeout;
              }
          }
          return std::cv_status::no_timeout;
      }
      

      1 这不是完全测试您的代码是否正常工作,而是 ,但是另一方面,如果你有一些线程问题,不能保证测试用例也能捕获它(在某些时候你必须决定等待响应的时间)。