多线程 C++ 中单例实例的正确互斥锁放置

问题描述 投票:0回答:1

我正在基于“Head First Design Patterns”书中的示例在 C++ 中实现线程安全的单例模式,该书最初在 Java 中介绍了该模式。我知道从 C++11 开始,静态局部变量的初始化是线程安全的。 但是,我不确定互斥锁相对于静态类方法中的 getInstance()

 调用的位置。 
mutex
 应该在 
getInstance()
 调用之前还是之后锁定?这是相关的代码片段:
我认为应该在之后,因为 
ChocolateBoilerSingleton::getInstance();
 是线程安全的。但我想在这里了解其他意见。

谢谢你。

#include <mutex> class ChocolateBoilerSingleton { public: ChocolateBoilerSingleton(const ChocolateBoilerSingleton&) = delete; ChocolateBoilerSingleton& operator=(const ChocolateBoilerSingleton&) = delete; static void fill() { auto& instance = ChocolateBoilerSingleton::getInstance(); std::lock_guard<std::mutex> lock(instance.m_mtx); if (instance.isEmpty()) { instance.m_empty = false; instance.m_boiled = false; } } static void boil() { auto& instance = ChocolateBoilerSingleton::getInstance(); std::lock_guard<std::mutex> lock(instance.m_mtx); if (!instance.isEmpty() && !instance.isBoiled()) { instance.m_boiled = true; } } static void drain() { auto& instance = ChocolateBoilerSingleton::getInstance(); std::lock_guard<std::mutex> lock(instance.m_mtx); if (!instance.isEmpty() && instance.isBoiled()) { instance.m_empty = true; } } public: static ChocolateBoilerSingleton& getInstance() { static ChocolateBoilerSingleton instance; return instance; } bool isBoiled() const { return this->m_boiled; } bool isEmpty() const { return this->m_empty; } // Data -------------------- private: ChocolateBoilerSingleton() : m_mtx(), m_empty(true), m_boiled(false) {} std::mutex m_mtx; bool m_empty; bool m_boiled; }; int main() { ChocolateBoilerSingleton::fill(); ChocolateBoilerSingleton::boil(); ChocolateBoilerSingleton::drain(); }
    
c++ multithreading thread-safety singleton
1个回答
0
投票

fill()

boil()
drain()
 不必是静态的。调用者应该使用 
getInstance()
 方法来获取对单例对象的引用,并使用该引用调用这些成员函数。

void fill() { std::lock_guard<std::mutex> lock(m_mtx); if (isEmpty()) { m_empty = false; m_boiled = false; } }
同样的事情适用于 

boil()

drain()
,你的 
main()
 函数将是这样的。

int main() { auto& boiler = ChocolateBoilerSingleton::getInstance(); boiler.fill(); boiler.boil(); boiler.drain(); return 0; }
还有一件事值得一提。

锁定你的const getter方法。这可能会很奇怪,因为你以只读方式访问m_boiled

m_empty
,但是
任何可以由另一个线程写入的数据都应该被锁定即使你正在阅读它。在您的情况下, fill()
boil()
 可以在您尝试读取时更新变量。

bool isBoiled() const { std::lock_guard<std::mutex> lock(m_mtx); return this->m_boiled; }
不要忘记将 

m_mtx

 标记为 
mutable
,因为 const 成员无法锁定互斥体。

mutable std::mutex m_mtx;
另外,你可以考虑使用

std::atomic<bool>

,可以去掉
mutex

© www.soinside.com 2019 - 2024. All rights reserved.