我的队列中有两个函数:
void push(const T& object)
{
std::lock_guard<std::mutex> lock(mutex);
queue.emplace(object);
conditionVariable.notify_all();
}
T waitAndPeek()
{
std::unique_lock<std::mutex> lock(mutex);
conditionVariable.wait(lock, [&] {
return !queue.empty();
});
return queue.front();
}
现在我有这样一个测试(GoogleTest):
std::thread t{[this] {
EXPECT_EQ(queue.waitAndPeek(), 66);
}};
std::this_thread::sleep_for(3000ms);
queue.push(66);
t.join();
您知道如何使该测试不依赖于实时吗?我不想等 2000 毫秒。
所以你想测试 wait() 阻塞后调用 Push() 的情况。
一种方法是引入一个侧通道让测试知道 wait() 已经开始:
bool is_waiting() {
std::unique_lock<std::mutex> lock(mutex);
return is_waiting_;
}
T waitAndPeek() {
std::unique_lock<std::mutex> lock(mutex);
is_waiting_ = true;
conditionVariable.wait(lock, [&] {
return !queue.empty();
});
is_waiting_ = false;
return queue.front();
}
然后用繁忙的循环代替睡眠:
while (!queue.is_waiting()) {}
保持睡眠但减少问题的另一种方法是反转执行线程,以便主测试线程在辅助线程推送时调用 waitAndPeek():
std::thread t{[this] {
std::this_thread::sleep_for(10ms);
queue.push(66);
}};
EXPECT_EQ(queue.waitAndPeek(), 66);
t.join();
这比第一种方法侵入性小,但仍然可能需要小睡一会儿。与第一种情况相比,睡眠时间可以减少,因为这里我们先尽快进入 wait(),然后等待推送,而在原始测试中需要更多时间才能调用 wait():线程需要生成,发生上下文切换,然后才进入 wait()。
请注意,可能需要某种形式的睡眠,例如睡觉。如果您在单核繁忙 CI 实例上运行,那么对于繁忙循环,您还可以考虑添加一个微小的睡眠或 thread_yield() 调用。