C 17原子和条件_Variable僵局
C++17 atomics and condition_variable deadlock
我有以下代码,该代码在评论的行上僵持。基本上F1和F2作为程序中的单个线程运行。F1期望我是1,并将其减少,并通知CV。F2期望我为0并将其递增,并通知CV。我假设如果F2增量到1,请致电CV.Notify((,则会发生僵局,然后F1读取i(0(的陈旧值,因为Mutex和i之间没有内存同步,然后再等待,然后永远不会被唤醒。向上。然后F2也进入睡眠状态,现在两个线程都在等待的简历,这将永远不会通知。
如何编写此代码,以免发生僵局?基本上,我想实现的目标是具有通过两个线程更新的一些原子状态。如果状态在其中一个线程中不正确,我不想旋转;相反,我想使用CV功能(或类似的内容(在值正确时唤醒线程。
我正在使用G -7用O3编译代码(尽管僵局都在O0和O3中发生(。
#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
std::atomic_size_t i{0};
std::mutex mut;
std::condition_variable cv;
void f1() {
while (1) {
{
std::unique_lock<std::mutex> lk(mut);
cv.wait(lk, []() { return i.load() > 0; }); // deadlocks
}
--i;
cv.notify_one();
std::cout << "i = " << i << std::endl; // Only to avoid optimization
}
}
void f2() {
while (1) {
{
std::unique_lock<std::mutex> lk(mut);
cv.wait(lk, []() { return i.load() < 1; }); // deadlocks
}
++i;
cv.notify_one();
std::cout << "i = " << i << std::endl; // Only to avoid optimization
}
}
int main() {
std::thread t1(f1);
std::thread t2(f2);
t1.join();
t2.join();
return 0;
}
编辑:COUT仅是为了避免编译器优化。
我认为问题是可以更改i
的值,并且在另一个线程评估return i.load() > 0;
后,但是在Lambda呼叫返回和CV简历等待之前,可以在间隔中调用notify_one
。这样,另一个线程就不会观察到原子变量的变化,也没有人可以唤醒它以再次检查。这可以通过锁定变量时锁定静音来解决,尽管这样做会破坏原子的目的。
我认为VTT的答案是正确的,只想显示会发生什么。首先,可以将代码重写为以下形式:
void f1() {
while (1) {
{
std::unique_lock<std::mutex> lk(mut);
while (i == 0) cv.wait(lk);
}
--i;
cv.notify_one();
}
}
void f2() {
while (1) {
{
std::unique_lock<std::mutex> lk(mut);
while (i >= 1) cv.wait(lk);
}
++i;
cv.notify_one();
}
}
现在,考虑以下时间行,i
最初是0
:
time step f1: f2:
========= ================= ================
1 locks mut
2 while (i >= 1) F
3 unlocks mut
4 locks mut
5 while (i == 0) T
6 ++i;
7 cv.notify_one();
8 cv.wait(lk);
9 unlocks mut(lk)
10 locks mut
11 while (i >= 1) T
12 cv.wait(lk);
有效地,f1
在i
为1
时等待。这两个线程现在都立即以阻止状态等待。
解决方案是将i
的修改放入锁定部分。然后,i
甚至不需要是原子变量。
当线程不拥有Mutex时,您可以调用cv.notify_one();
。它可能导致通知被发送为空。想象一下f2
在f1
之前开始。f2
调用cv.notify_one();
,但f1
尚未在cv.wait
中。
获得的Mutex保证f2
在std::unique_lock<std::mutex> lk(mut)
中或等待通知。
#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
std::atomic_size_t i{0};
std::mutex mut;
std::condition_variable cv;
void f1() {
while (1) {
std::size_t ii;
{
std::unique_lock<std::mutex> lk(mut);
cv.wait(lk, []() { return i.load() > 0; });
ii = --i;
cv.notify_one();
}
std::cout << "i = " << ii << std::endl;
}
}
void f2() {
while (1) {
std::size_t ii;
{
std::unique_lock<std::mutex> lk(mut);
cv.wait(lk, []() { return i.load() < 1; });
ii = ++i;
cv.notify_one();
}
std::cout << "i = " << ii << std::endl;
}
}
int main() {
std::thread t1(f1);
std::thread t2(f2);
t1.join();
t2.join();
return 0;
}
btw std::atomic_size_t i
可以是std::size_t i
。
,因为 i 是原子,因此无需使用互斥s守护其修改。
f1 和 f2 在条件变量上的等待,除非发生虚假唤醒,否则请等待,因为条件变量永远不会通知。由于不能保证虚假唤醒,我建议在等待条件变量之前检查条件,并最终通知其他线程的条件变量。
您的代码还有另一个问题。这两个功能 f1 和 f2 将永远不会结束。因此,您的 Main 功能将等待加入其线程。
- C++ Singleton - Prevent ::instance() to variable
- 如何通过多类"Union variable" (sfml) 使用轮询事件
- 将成员函数作为构造函数参数调用时出错 "Variable is not a type name"
- 为什么我会收到"Run-Time Check Failure #2 - Stack around the variable 'pr' was corrupted"错误?
- C++:寻找"returning address of local variable..."的更正
- C++ - 在我尝试制作一个简单的计算器时有一个"uninitialized local variable y used"警告
- Visual Studio Code "variable " u8 的 C/C++ 扩展名 " " 不是类型名称"
- C++ "Using Uninitialized Memory.. (variable name) "
- cudaMallocManaged for host-initiated variable
- Cppcheck "Reference to auto variable returned"失败的原因?
- OpenCV 和 C++:"Can't resolve variable 'Mat`"
- 是否有用于"go to variable type definition" C++中"auto"变量的 Visual Studio 键盘快捷方式?
- 冲刺错误"variable may be unsafe" (C4996)...选择?
- Visual Studio生成"Error: uninitialized local variable 'x' "而在线编译器不会 - 为什么?
- GUID variable for JPEG,BMP,PNG
- 如何修复初始化后'stack around variable was corrupted.'变量未更改为分配的值
- C++:从 "const variable*" 转换为"uint32"会失去精度
- C++17 'inline variable' vs 'extern' 关键字继承自 C 的新功能
- Tensorflow C++ API assign variable value
- 明确定义'static const variable in a struct'对C++ 11 及以上有什么影响吗?