我正在处理的一个项目使用多个线程来处理一组文件。每个线程都可以将文件添加到要处理的文件列表中,因此我将(我认为是)一个线程安全队列放在一起。相关部分如下:
// qMutex is a std::mutex intended to guard the queue
// populatedNotifier is a std::condition_variable intended to
// notify waiting threads of a new item in the queue
void FileQueue::enqueue(std::string&& filename)
{
std::lock_guard<std::mutex> lock(qMutex);
q.push(std::move(filename));
// Notify anyone waiting for additional files that more have arrived
populatedNotifier.notify_one();
}
std::string FileQueue::dequeue(const std::chrono::milliseconds& timeout)
{
std::unique_lock<std::mutex> lock(qMutex);
if (q.empty()) {
if (populatedNotifier.wait_for(lock, timeout) == std::cv_status::no_timeout) {
std::string ret = q.front();
q.pop();
return ret;
}
else {
return std::string();
}
}
else {
std::string ret = q.front();
q.pop();
return ret;
}
}
但是,我偶尔会在 if (...wait_for(lock, timeout) == std::cv_status::no_timeout) { }
块内发生段错误,并且 gdb 中的检查表明由于队列为空而发生段错误。这怎么可能?据我了解, wait_for
仅在收到通知时才返回 cv_status::no_timeout
,并且这只发生在 FileQueue::enqueue
刚刚将新项目推送到队列之后。
原文由 Matt Kline 发布,翻译遵循 CC BY-SA 4.0 许可协议
根据标准
condition_variables
允许虚假唤醒,即使事件没有发生。如果发生虚假唤醒,它将返回cv_status::no_timeout
(因为它是唤醒而不是超时),即使它没有被通知。正确的解决方案当然是在继续之前检查唤醒是否真的合法。详细信息在标准§30.5.1 [thread.condition.condvar] 中指定: