1. 程式人生 > >muduo_base程式碼剖析之執行緒安全的Sigleton模式

muduo_base程式碼剖析之執行緒安全的Sigleton模式

預備知識

  1. 什麼是單例模式?
    一個類有且僅有一個例項,並且對外提供統一的訪問該例項的介面
  2. 單例模式分為餓漢式、懶漢式,對於餓漢式要保證執行緒安全需要使用double-check lock機制
  3. muduo中實現的執行緒安全的Sigleton類,沒使用鎖,而使用效率更高的執行緒一次性初始化函式

上述講解的內容比較基礎,不懂的同學可以自行百度

moduo的執行緒安全的Sigleton類的原始碼分析

  1. 使用一次性初始化函式pthread_once(&ponce_, &Singleton::init),保證value_ = new T()語句只能被執行一次,即只建立一個
  2. 使用模板機制,Singleton將各種類T包裝成執行緒安全的單例類
  3. atexit
    ::atexit(destroy);
    在init 函式內註冊destroy,在程式結束時會呼叫destroy,在destroy內部delete value_;
  4. 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());
}