C.M*_*.M. 6 c++ multithreading c++17
下面是一个 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();
Run Code Online (Sandbox Code Playgroud)
据我了解,这是使用原子标志和条件变量来实现目标的有效方法。例如不需要使用std::memory_order_seq_cst。
是否可以进一步放宽此代码?例如:
std::memory_order_relaxed在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还写入将由读取器线程读取的其他对象,则您需要获取/释放语义以确保数据正确可见:读取器线程可能会锁定互斥体并看到新的互斥体。在编写器线程锁定互斥体ready_flag 之前的值,在这种情况下,互斥体本身将不提供排序保证。
如果设置 的线程没有触及其他数据ready_flag,或者该数据受到另一个互斥体或其他同步机制的保护,那么您可以在任何地方使用,因为您只关心它本身memory_order_relaxed的值,而不是顺序ready_flag任何其他写入。
atomic_thread_fence在任何情况下都对这段代码没有帮助。如果您使用条件变量,则lock_guard{m}是必需的。