muduo_base程式碼剖析之執行緒安全的Sigleton模式
阿新 • • 發佈:2018-11-26
預備知識
- 什麼是單例模式?
一個類有且僅有一個例項,並且對外提供統一的訪問該例項的介面 - 單例模式分為餓漢式、懶漢式,對於餓漢式要保證執行緒安全需要使用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());
}