我写了一个共享优先级队列类
要发出停止提供数据的信号,我使用方法Cancel()
,它将符号done
设置为false,并且不允许应用程序写入/读取队列中的任何数据。我不确定将std::atomic<bool>
与std::mutex
和std::condition_variable
结合使用。我不确定,如果我的解决方案是线程安全的,或者竞争条件可能发生:
Enqueue
方法的原始版本是:
std::deque<T> deque;
std::mutex mtx;
std::condition_variable cv;
std::atomic<bool> done;
SharedPriorityQueue() : done(false)
{
}
~SharedPriorityQueue()
{
Cancel();
}
void Enqueue(T item)
{
if (done)
{
return;
}
std::lock_guard<std::mutex> lock(mtx);
deque.push_back(item);
cv.notify_one();
}
但是,可以通过互斥锁将变量done
(原子bool)与锁定机制分开?
要取消队列,我使用这种结构:
void Cancel()
{
if (done)
{
return;
}
done = true;
cv.notify_all();
}
下面这些设计的最佳解决方案是什么?
// A)
void Enqueue(T item)
{
if (done)
{
return;
}
{
std::lock_guard<std::mutex> lock(mtx); // lock is released before notify call
deque.push_back(item);
}
cv.notify_one();
}
// B)
void Enqueue(T item)
{
{
std::lock_guard<std::mutex> lock(mtx); // done is atomic bool and protected by the lock along with data (deque)
if (done) // atomic bool
{
return;
}
deque.push_back(item);
}
cv.notify_one();
}
// C)
void Enqueue(T item)
{
{
std::lock_guard<std::mutex> lock(mtx); // done is NOT atomic bool and is protected by the lock along with data (deque)
if (done) // simple bool
{
return;
}
deque.push_back(item);
}
cv.notify_one();
}
等待员工:
bool Dequeue(T& item)
{
std::unique_lock<std::mutex> lock(mtx);
while (!done && deque.empty())
{
cv.wait(lock);
}
if (!deque.empty())
{
item = deque.front();
deque.pop_front();
}
if (done)
{
return false;
}
return true;
}
答案 0 :(得分:1)
为了确保正确性,您必须修改与condition_variable
在.wait(...)
中获取锁定的“条件”相关的数据。
虽然不是规范性的,但我能找到的最明确的陈述是:
即使共享变量是原子的,也必须在 互斥为了正确地将修改发布到等待中 线程。
此处:http://en.cppreference.com/w/cpp/thread/condition_variable
它非常明确地回答了您的问题!
Cancel()
需要暂停mtx
。在这一点上,它使原子停止帮助。
我不确定规范性声明的位置,但我确实知道MSVC ++社区就是这种情况。
您notify_one()
(或notify_all()
)时无需保持锁定,但在修改“共享状态”(在本例中为队列或标志)时必须保留锁定。
答案 1 :(得分:0)
正常/最常见的情况可能是队列准备就绪(未完成),而完成状态可能仅在终止期间使用。通过使用原子来优化完成的情况可能没什么意义。
您需要了解要优化的内容并使用分析器。