预备知识
- 什么是单例模式?
一个类有且仅有一个实例,并且对外提供统一的访问该实例的接口 - 单例模式分为饿汉式、懒汉式,对于饿汉式要保证线程安全需要使用double-check lock机制
- muduo中实现的线程安全的Sigleton类,没使用锁,而使用效率更高的线程一次性初始化函数
上述讲解的内容比较基础,不懂的同学可以自行百度
moduo的线程安全的Sigleton类的源码分析
- 使用一次性初始化函数pthread_once(&ponce_, &Singleton::init),保证value_ = new T()语句只能被执行一次,即只创建一个
- 使用模板机制,Singleton将各种类T包装成线程安全的单例类
- atexit
::atexit(destroy);
在init 函数内注册destroy,在程序结束时会调用destroy,在destroy内部delete value_; - typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
假设class A; A* p; delete p; 现在A只是前向声明,是不完全类型,那么delete p会出问题,但在编译时只是报警告。
sizeof(A) == 0; 故 typedef char T_must[-1]; 在编译时就会出错。
template<typename T>
class Singleton : noncopyable
{
private:
static pthread_once_t ponce_;
static T* value_; //唯一的实例对象指针
public:
Singleton() = delete;
~Singleton() = delete;
//即使多个线程都调用了instance()函数
//pthread_once还是能保证init()函数只被执行一次
//这种方式比使用锁的效率高
static T& instance()
{
pthread_once(&ponce_, &Singleton::init);
assert(value_ != NULL);
return *value_;
}
private:
static void init() //创建对象
{
value_ = new T();
if (!detail::has_no_destroy<T>::value)
{
::atexit(destroy); //在整个程序结束时,调用销毁函数(因此不需要手动销毁)
}
}
static void destroy()
{
typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
T_must_be_complete_type dummy; (void) dummy;
delete value_;
value_ = NULL;
}
};
template<typename T>
pthread_once_t Singleton<T>::ponce_ = PTHREAD_ONCE_INIT;
template<typename T>
T* Singleton<T>::value_ = NULL;
测试代码
单例模式的使用,就是将自定义的类,作为模板参数T传给Singleton
#include <muduo/base/Singleton.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Thread.h>
#include <stdio.h>
class Test : muduo::noncopyable
{
public:
Test()
{
printf("tid=%d, constructing %p\n", muduo::CurrentThread::tid(), this);
}
~Test()
{
printf("tid=%d, destructing %p %s\n", muduo::CurrentThread::tid(), this, name_.c_str());
}
const muduo::string& name() const { return name_; }
void setName(const muduo::string& n) { name_ = n; }
private:
muduo::string name_;
};
void threadFunc()
{
//打印name
printf("tid=%d, %p name=%s\n",
muduo::CurrentThread::tid(),
&muduo::Singleton<Test>::instance(),
muduo::Singleton<Test>::instance().name().c_str());
//更改name = "only one, changed";
muduo::Singleton<Test>::instance().setName("only one, changed");
}
int main()
{
//使用Singleton类,将自定义的Test封装成线程安全的单例类
muduo::Singleton<Test>::instance().setName("only one");
muduo::Thread t1(threadFunc); //子线程
t1.start();
t1.join();
//打印name,因为单例模式,共享的是同一个实例,因此打印结果name为修改后的值only one, changed
printf("tid=%d, %p name=%s\n", //主线程
muduo::CurrentThread::tid(),
&muduo::Singleton<Test>::instance(),
muduo::Singleton<Test>::instance().name().c_str());
}