下面是一个 C++17 代码片段,其中一个线程等待另一个线程到达某一阶段:
std::condition_variable cv;
std::atomic<bool> ready_flag{false};
std::mutex m;
// thread 1
... // start a thread, then wait for it to reach certain stage
auto lock = std::unique_lock(m);
cv.wait(lock, [&]{ return ready_flag.load(std::memory_order_acquire); });
// thread 2
... // modify state, etc
ready_flag.store(true, std::memory_order_release);
std::lock_guard{m}; // NOTE: this is lock immediately followed by unlock
cv.notify_all();
据我了解,这是使用原子标志和条件变量来实现目标的有效方法。例如不需要使用std::memory_order_seq_cst
here.
是否可以进一步放宽此代码?例如:
- 也许使用
std::memory_order_relaxed
in ready_flag.load()
- 也许使用
std::atomic_thread_fence()
代替std::lock_guard{m};
首先:这段代码确实有效。这lock_guard
在...之前notify_one
调用确保等待线程将看到正确的值ready_flag
当它唤醒时,无论是由于虚假唤醒,还是由于调用notify_one
.
其次:如果只访问ready_flag
是这里显示的,然后使用atomic
太过分了。将写入移动到ready_flag
的范围内lock_guard
在编写器线程上并使用更简单、更传统的模式。
如果你坚持这个模式,那么你是否可以使用memory_order_relaxed
取决于您需要的排序语义。
如果设置的线程ready_flag
还写入将由读取器线程读取的其他对象,然后您need获取/释放语义以确保数据正确可见:读取器线程可以锁定互斥体并查看新值ready_flag
before编写器线程已锁定互斥锁,在这种情况下,互斥锁本身将不提供排序保证。
如果设置该线程的线程没有触及其他数据ready_flag
,或者数据受到另一个互斥锁或其他同步机制的保护,那么您可以使用memory_order_relaxed
无处不在,因为它只是ready_flag
您关心的是它本身,而不是任何其他写入的顺序。
atomic_thread_fence
在任何情况下都对这段代码没有帮助。如果您使用条件变量,则lock_guard{m}
是必须的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)