(以下全是线程安全)
最简单的实现:加锁
#include <iostream>
#include <mutex>
using namespace std;
std::mutex mt;
class Singleton
{
private:
Singleton(){}
public:
static Singleton* instance()
{
mt.lock(); // 加锁
if(_instance == 0)
_instance = new Singleton();
mt.unlock(); // 解锁
return _instance;
}
private:
static Singleton* _instance;
};
Singleton* Singleton::_instance = 0;
缺点
每次获得对象的时候都要加锁,性能差。
双重锁
#include <iostream>
#include <mutex>
using namespace std;
std::mutex mt;
class Singleton
{
private:
Singleton(){}
public:
static Singleton* instance()
{
if(_instance == 0)
{
mt.lock();
if(_instance == 0)
_instance = new Singleton();
mt.unlock();
}
return _instance;
}
private:
static Singleton* _instance;
public:
int atestvalue;
};
Singleton* Singleton::_instance = 0;
缺点:
new的步骤是:
- 分配空间
- 构造对象
- 将指针指向这个空间
其中步骤2和步骤3的顺序可能会发生改变,存在一种情况,new时执行顺序是132,指针指向后线程被切换,此时另一个线程执行到第一个if时发现对象已创建,但此时对象还未构造,就会出现错误。
C++11版本方法
class Singleton
{
public:
// 注意返回的是引用
static Singleton& getInstance()
{
static Singleton value; //静态局部变量,是线程安全的
return value;
}
private:
Singleton() = default;
Singleton(const Singleton& other) = delete; //禁止使用拷贝构造函数
Singleton& operator=(const Singleton&) = delete; //禁止使用拷贝赋值运算符
};
缺点:只能C++11版本后使用
万能写法(源自brpc)
class Singleton
{
public:
static inline Singleton* singleton() {
//注意这几个原子变量的参数
ObjectPool* p = _singleton.load(butil::memory_order_consume);
if (p) {
return p;
}
pthread_mutex_lock(&_singleton_mutex);
p = _singleton.load(butil::memory_order_consume);
if (!p) {
p = new Singleton();
_singleton.store(p, butil::memory_order_release);
}
pthread_mutex_unlock(&_singleton_mutex);
return p;
}
private:
Singleton() = default;
Singleton(const Singleton& other) = delete; //禁止使用拷贝构造函数
Singleton& operator=(const Singleton&) = delete; //禁止使用拷贝赋值运算符
atomic<Singleton*> _singleton;
mutex _singleton_mutex;
};