C++ 使用std::atomic的正确性<;布尔>;与std::mutex结合使用
我已经编写了一个共享优先级队列类。C++ 使用std::atomic的正确性<;布尔>;与std::mutex结合使用,c++,multithreading,c++11,stdatomic,stdmutex,C++,Multithreading,C++11,Stdatomic,Stdmutex,我已经编写了一个共享优先级队列类。 为了发出停止提供数据的信号,我使用方法Cancel(),该方法将符号done设置为false,并且不允许应用程序从队列中写入/读取任何数据。我不确定是否将std::atomic与std::mutex和std::condition\u变量组合使用。我不确定,如果我的解决方案是线程安全的,或者可能发生争用情况: Enqueue方法的原始版本为: std::deque<T> deque; std::mutex mtx; std::condition_va
为了发出停止提供数据的信号,我使用方法
Cancel()
,该方法将符号done
设置为false,并且不允许应用程序从队列中写入/读取任何数据。我不确定是否将std::atomic
与std::mutex
和std::condition\u变量组合使用。我不确定,如果我的解决方案是线程安全的,或者可能发生争用情况:
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();
}
以下设计的最佳解决方案是什么
// 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();
}
/A)
无效排队(T项)
{
如果(完成)
{
返回;
}
{
std::lock_guard lock(mtx);//在notify调用之前释放锁
德克。推回(项目);
}
cv.通知_one();
}
//(B)
无效排队(T项)
{
{
std::lock_guard lock(mtx);//done是原子bool,由锁和数据保护(deque)
if(done)//原子布尔
{
返回;
}
德克。推回(项目);
}
cv.通知_one();
}
//(C)
无效排队(T项)
{
{
std::lock_guard lock(mtx);//done不是原子bool,它与数据一起受锁保护(deque)
if(done)//简单bool
{
返回;
}
德克。推回(项目);
}
cv.通知_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;
}
bool出列(T&item)
{
std::唯一锁(mtx);
而(!done&&deque.empty())
{
cv.等待(锁定);
}
如果(!deque.empty())
{
item=deque.front();
deque.pop_front();
}
如果(完成)
{
返回false;
}
返回true;
}
正常/最常见的情况可能是队列已就绪(未完成),而完成状态可能仅在终止期间使用。使用原子函数为已完成的情况进行优化可能没有什么意义
您需要了解您正在优化什么,并使用探查器。正常/最常见的情况可能是队列已就绪(未完成),而完成状态可能仅在终止期间使用。使用原子函数为已完成的情况进行优化可能没有什么意义
您需要了解您正在优化的内容并使用探查器。为了确保正确性,您必须修改与持有锁的“条件”相关的数据,该锁是条件变量在中获取的。等待(…)
虽然不规范,但我能找到的最清晰的陈述是:
即使共享变量是原子变量,也必须在
互斥,以便将修改正确发布到等待
线
在这里:
它非常明确地回答了您的问题强>
Cancel()
需要按住mtx
。在这一点上,使它原子停止帮助
我不确定规范性声明在哪里,但我知道MSVC++社区就是这样
当您notify_one()
(或notify_all()
)时,您不需要持有锁,但当您修改“共享状态”(在本例中为队列或标志)时,您必须持有锁。为了确保正确性,您必须修改与“条件”相关的数据持有条件变量
在.wait(…)
中获取的锁
虽然不规范,但我能找到的最清晰的陈述是:
即使共享变量是原子变量,也必须在
互斥,以便将修改正确发布到等待
线
在这里:
它非常明确地回答了您的问题强>
Cancel()
需要按住mtx
。在这一点上,使它原子停止帮助
我不确定规范性声明在哪里,但我知道MSVC++社区就是这样
当您
notify_one()
(或notify_all()
)时,您不需要持有锁,但当您修改“共享状态”(在本例中为队列或标志)时,您必须持有锁。请缩小问题范围。似乎有些缺乏焦点你是什么意思?问题中有什么不清楚?我想具体说明一个问题……你在标题中问的是,在线程库中使用原子的“正确性”,这已经很模糊了。然后你在正文中问了两个问题:在条件变量之前是否需要锁定;在许多实现中,哪一个是“更好的”,哪一个也是模糊的。选择一个,并且尽可能客观地具体说明你想要的答案希望现在更好;-)我已经排除了“在条件变量之前是否需要锁定”这个问题。这段代码什么都不做,因为条件变量上没有等待任何东西。您的代码需要是最小的和完整的。这并不容易,但如果不能做到最小和完整,就意味着在理解你的问题之前,我必须编写你缺少的代码。请缩小问题的范围。似乎有些缺乏焦点你是什么意思?问题中有什么不清楚?我想具体说明一个问题……你在标题中问的是,在线程库中使用原子的“正确性”,这已经很模糊了。然后你在正文中问了两个问题:在条件变量之前是否需要锁定;在许多实现中,哪一个是“更好的”,哪一个也是模糊的。选择一个,并且尽可能客观地具体说明你想要的答案希望现在更好;-)我已经排除了“在条件变量之前是否需要锁定”这个问题。这段代码什么都不做,因为条件变量上没有等待任何东西。您的代码需要是最小的和完整的。这并不容易,但如果不能做到最小和完整,就意味着在理解您的问题之前,我必须编写您缺少的代码
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;
}