我很难理解导致此测试偶尔死锁的实际顺序。抱歉,示例有点长,很难可靠地重现我的问题。
理想情况下,
inc_thread
增量data
然后设置flag
并通知dec_thread
。
dec_thread
等待flag
设置且data
非零,然后减少data
并清除flag
。
REP_COUNT
对每个线程重复此操作次,并以data
结尾0
。
如果flag.store(false, mem_order);
使用std::memory_order_acq_rel
这个,偶尔会出现死锁,dec_thread
等待的flag
时间data
不为零。为什么?
即使所有其他操作都是隐式seq_cst
或显式的,也会发生这种情况acq_rel
。
#include <atomic>
#include <thread>
#include <cstdint>
void race_test() {
// always works
//constexpr auto mem_order = std::memory_order_seq_cst;
// occasionally deadlocks
constexpr auto mem_order = std::memory_order_acq_rel;
std::atomic<std::size_t> data = 0;
std::atomic_bool flag = false;
constexpr std::size_t REP_COUNT = 2000;
std::thread dec_thread{[&] {
for (std::size_t i = 0; i != REP_COUNT; ++i) {
while (data.load() == 0) {
flag.wait(false);
flag.store(false, mem_order);
}
--data;
}
}};
std::thread inc_thread{[&] {
for (std::size_t i = 0; i != REP_COUNT; ++i) {
++data;
flag.store(true);
flag.notify_one();
}
}};
inc_thread.join();
dec_thread.join();
}
int main()
{
for (std::size_t i = 0; i != 1000; ++i)
race_test();
}
当改变mem_order
一切memory_order_seq_cst
似乎总能正常工作。
我所能想象的问题在于,它的flag.store(false, mem_order);
顺序是在之后data.load()
(并且flag.store(true);
在中inc_thread
),但难道不应该被阻止吗acq_rel
?
据我了解,memory_order_seq_cst
这只能防止与其他操作重新排序memory_order_seq_cst
,但难道不应该让一切都memory_order_acq_rel
解决这个问题吗?(事实并非如此)
假设更改flag.store(false)
为seq_cst
甚至足以使此问题正确解决,有人能解释一下原因吗?如果有人能为每个原子操作说明最“宽松”的顺序,同时仍保持无竞争,我将不胜感激。
谢谢。