問題描述
我正在處理的一個項目使用多個線程來處理一組文件.每個線程都可以將文件添加到要處理的文件列表中,所以我把(我認為是)一個線程安全隊列放在一起.相關部分如下:
A project I'm working on uses multiple threads to do work on a collection of files. Each thread can add files to the list of files to be processed, so I put together (what I thought was) a thread-safe queue. Relevant portions follow:
// 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, ti??meout) == std::cv_status::no_timeout) { }
塊中出現段錯誤,并且在 gdb 中檢查表明由于隊列為空,出現段錯誤.這怎么可能?我的理解是 wait_for
只在收到通知時返回 cv_status::no_timeout
,并且這應該只在 FileQueue::enqueue
之后發生剛剛將一個新項目推送到隊列中.
However, I am occasionally segfaulting inside the if (...wait_for(lock, timeout) == std::cv_status::no_timeout) { }
block, and inspection in gdb indicates that the segfaults are occurring because the queue is empty. How is this possible? It was my understanding that wait_for
only returns cv_status::no_timeout
when it has been notified, and this should only happen after FileQueue::enqueue
has just pushed a new item to the queue.
推薦答案
根據標準 condition_variables
允許虛假喚醒,即使事件沒有發生.在虛假喚醒的情況下,它會返回 cv_status::no_timeout
(因為它喚醒而不是超時),即使它沒有被通知.正確的解決方案當然是在繼續之前檢查喚醒是否真的合法.
According to the standard condition_variables
are allowed to wakeup spuriously, even if the event hasn't occured. In case of a spurious wakeup it will return cv_status::no_timeout
(since it woke up instead of timing out), even though it hasn't been notified. The correct solution for this is of course to check if the wakeup was actually legit before proceding.
細節在標準 §30.5.1 [thread.condition.condvar] 中指定:
The details are specified in the standard §30.5.1 [thread.condition.condvar]:
——當通過調用 notify_one()、調用 notify_all()、abs_time 指定的絕對超時 (30.2.4) 或虛假發出信號時,函數將解除阻塞.
—The function will unblock when signaled by a call to notify_one(), a call to notify_all(), expiration of the absolute timeout (30.2.4) speci?ed by abs_time, or spuriously.
...
返回: cv_status::timeout 如果 abs_time 指定的絕對超時 (30.2.4) 過期,則為 cv_status::no_timeout.
Returns: cv_status::timeout if the absolute timeout (30.2.4) speci?edby abs_time expired, other-ise cv_status::no_timeout.
這篇關于C++11 線程安全隊列的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!