我有一个类有一个等待队列被填满的 wait
函数。一旦队列非空,condition_variable
就会收到通知,等待函数会清空队列并处理其中的元素。
我想在单元测试中验证此 wrapper::wait
调用(检查队列是否为非空)实际上阻塞了线程。我有一个不稳定的设置,其中包含一个 atomic_bool
,它是在 wait 调用之后设置的,我天真地认为执行以下序列会起作用。
- 使用调用
wait
的函数创建线程,然后设置atomic_bool
。 - 创建线程后,验证
atomic_bool
有开始值 - 在主线程中将某些内容插入队列,使
atomic_bool
更改其他线程的值。 - 在主线程中验证更改的值。
缺少两个同步点:两个“验证”不能保证在我希望它们发生时发生:
- 我无法验证是否已在线程中调用了 wait。
- 我无法验证线程是否有机会更改
atomic_bool
。
有没有什么办法可以在不增加一个或多个额外的condition_variables
/mutexes
来同步操作的情况下解决这个问题?
最佳答案
这真的是使用 std::condition_variable
的完美场景, 但无论如何:您可以将多个同步点与 std::atomic<int> gate
一起使用.各种值将对测试的不同阶段进行编码。等待值更改的时间超过某个恒定时间 T 将被视为测试用例失败1。
-
gate == 0
: 辅助线程未初始化。 -
gate == 1
: 辅助线程已初始化,但尚未响应。 -
gate == 2
: 辅助线程已初始化并正在运行,可以假设已调用wait
. -
gate == 3
: 主线程已向队列添加内容,辅助线程可能仍在等待。 -
gate == 4
: 辅助线程已退出wait
并设置最后一个值。
如果愿意,您可以省略几个同步点。
主线程
- 设置
gate
到 0。 - 生成辅助线程。
- 设置
gate
到 1。 - 等待 T
gate
变成2;失败时:“辅助线程从未达到wait
”。 - 向队列添加一些东西,设置
gate
到 3. - 等待 T
gate
变成4;失败时:“辅助线程从未退出wait
”。 - 等待 T 次线程加入;失败时:“辅助线程在退出前挂起”。
辅助线程
- 等待 T
gate
变成 1;失败时“主线程在产生后挂起”。 - 设置
gate
到 2. 调用wait
. - (调用
wait
退出;队列已处理)。 - 等待 T
gate
变成3;失败时“回到过去”。 - 设置
gate
到 4 并退出。
要实现“等待和检查”部分,您可以使用 compare_exchange
和 std::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 这不是测试准确您的代码是否工作,而是测试它是否工作足够快,但另一方面,如果您有一些线程问题,也不能保证测试用例会捕获它(在某些时候,您必须决定等待响应的时间)。
关于c++ - 我如何对 condition_variable::wait 周围的包装器进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41131112/