CppCoreGuidelines CP.111 如果需要双次检查锁,请使用常用的设计模式
19 July 2023
“Use a conventional pattern if you really need double-checked locking”
理由
双次检查锁的实现很容易出错。如果你需要自己编写双次检查锁的代码,考虑规则 CP.110 不要自己写双次检查锁的初始化代码 和 CP.100 除非你确定需要,尽量不要用无锁编程。如果你的确需要,考虑常用的设计模式。
如果非线程安全的行为很难或很少出现,并且有更快的线程安全测试进行保障的时候,使用双次检查锁模式并不违反规则CP.110 不要自己写双次检查锁的初始化代码。
坏例子
使用 volatile
并不能确保先进性检查后执行的代码能够线程安全。请查看
CP.200: 只使用 volatile
访问非 C++ 内存。
mutex action_mutex; volatile bool action_needed; if (action_needed) { std::lock_guard<std::mutex> lock(action_mutex); if (action_needed) { take_action(); action_needed = false; } }
好例子
mutex action_mutex; atomic<bool> action_needed; if (action_needed) { std::lock_guard<std::mutex> lock(action_mutex); if (action_needed) { take_action(); action_needed = false; } }
如果获取加载的方式比顺序一致加载效率更好的话,优化的内存顺序可能有些好处。
mutex action_mutex; atomic<bool> action_needed; if (action_needed.load(memory_order_acquire)) { lock_guard<std::mutex> lock(action_mutex); if (action_needed.load(memory_order_relaxed)) { take_action(); action_needed.store(false, memory_order_release); } }