C++ 线程安全的singleton如何实现
来源:互联网 发布:facebook广告优化技巧 编辑:程序博客网 时间:2024/04/24 19:29
线程安全的C++ 单例
饿汉模式
必须在main开始前就初始化好,于是,需要一个静态变量。可以是对象,也可以是对象的指针。如果是指针,在main开始前new一下就行。由于这个静态变量只会被单例类使用,所以最好不是静态全局变量,而是类的静态成员变量。
// 对象版// singleton.hclass Singleton { private: Singleton() {} Singleton(const Singleton &); Singleton& operator= (const Singleton &); static Singleton s; public: static Singleton *GetInstance() { return &s; }};// singleton.ccSingleton Singleton::s;
还有:
//指针版。没有析构逻辑,会造成泄露。// singleton.hclass Singleton { private: Singleton() {} Singleton(const Singleton &); Singleton& operator= (const Singleton &); static Singleton *p; public: static Singleton *GetInstance() { return p; }};// singleton.ccSingleton *Singleton::p = new Singleton;
优点:
线程安全
实现简单,容易维护
缺点:
不适合部分场景。如:因为性能问题,希望懒加载;需要运行时才能知道,是否生成实例
由于在main开始前就必须初始化,几乎不可能给类传入任何参数。
懒汉模式
1.返回引用
使用局部静态变量。局部静态变量的初始化是线程安全的,这一点由编译器保证(http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00265.html,这是一个GCC的patch,专门解决这个问题)。会在程序退出的时候自动销毁。
见: http://stackoverflow.com/questions/270947/can-any-one-provide-me-a-sample-of-singleton-in-c/271104#271104
适合C++11,保证静态局部变量的初始化是线程安全的。如果是C++98就不能用这个方法。
class S{ public: static S& getInstance() { static S instance; return instance; } private: S() {} S(S const&); // Don't Implement. void operator=(S const&); // Don't implement };
加锁。线程安全,但每次都有开销。
// singleton.hclass Singleton { public: static Singleton *GetInstance() { lock(); if (p == NULL) { p = new Singleton; } return p; } private: static Singleon *p; Singleton() {} Singleton(const Singleton &); Singleton& operator= (const Singleton &);};// singleton.ccSingleton *Singleton::p = NULL;
pthread_once
陈硕推荐的做法
class Singleton { public: static Singleton *GetInstance() { pthread_once(&ponce_, &Singleton::init); return value_; } private: Singleton() {} Singleton(const Singleton &); Singleton& operator= (const Singleton &); static void init() { value_ = new T(); } static pthread_once_t ponce_; static Singleton *value_;};pthread_once_t SIngleton::ponce_ = PTHREAD_ONCE_INIT;Singleton* Singleton::value_ = NULL;
DCL
double check locking.只能用内存屏障,其他做法都是有问题的。
参见论文: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
普通的double check之所以错,是因为乱序执行和多处理器下,不同CPU中间cache刷往内存并对其他CPU可见的顺序无法保障(cache coherency problem)。 Singleton<T> *p = new Singleton<T>;
,那么实际有3步:
1. 分配内存
2. 构造
3. 赋值给p
2和3的顺序是未定的(乱序执行!)。因此,如果直接赋值给p那么很可能构造还没完成。此时另一个线程调用GetInstance,在lock外面check了一下,发现p!=NULL,于是直接返回p,使用了未初始化完成的实例,跪了。
那么,如果用中间变量转一下呢?用tmp_p转了下以后,tmp_p赋值给p的时候,显然p指向的实例是构造完成了的。然而,这个tmp_p在编译器看来明显没什么用,会被优化掉。
最佳实践?
1.eager initialize
2.次次加锁,但每个线程缓存了返回的指针,调用一次有用缓存的指针即可。
3.pthread_once
- C++ 线程安全的singleton如何实现
- c# SingleTon的线程安全(存档)
- 线程安全的C++的Singleton实现
- singleton 线程安全的singleton
- C++实现线程安全的Singleton
- 线程安全的 C++ Singleton 实现
- 线程安全的singleton
- 线程安全的singleton
- 线程安全的singleton
- Singleton的安全实现!
- 线程安全的Singleton模式的Java实现
- 线程安全的singleton实例
- singleton模式四种线程安全的实现
- singleton模式四种线程安全的实现
- singleton模式四种线程安全的实现
- singleton模式四种线程安全的实现
- 如何创建线程安全的单例模式?线程安全的Singleton!
- 如何正确实现多线程安全的singleton patterns
- Android Studio 各种快捷键
- JNI用javah命令生成头文件
- 算法导论第三版习题6.4
- UVA 12558(p216)----Eg[y]ptian Fractions(HARD version)
- File
- C++ 线程安全的singleton如何实现
- php和mysql开发防注入函数
- 数据结构算法之排序系列Java、C源码实现(5)--冒泡排序
- Android从一个应用程序启动第三方应用app
- UVA 12563(p274)----Jin Ge Jin Qu [h]ao
- nefu A Simple Math Problem 459 (矩阵连乘)
- 视觉SLAM漫谈
- eclipse中初探javadoc
- 对话框样式的Activity 和 重复点击view