1. 一般singleton寫法
單例模式即要求只有在第一次呼叫的時候創建該物件,主要分為以下兩條路(回傳指標還是參考),回傳參考可以防止使用中delete instance導致物件被提前銷毀:
- private包含static指標以及建構式,public里創建函式,通過呼叫其建構式,回傳該指標
- private包含static指標以及建構式,public里創建函式,通過呼叫其建構式,回傳該參考
- private包含建構式,public里創建函式,該函式里設定static變數實體,回傳其參考
實際寫單例的時候,需要考慮兩個問題
- 是否需要delete,如何delete
- 是否考慮競爭
如果使用一般的創建函式,new一個物件,則需要考慮如何delete,否則會出現記憶體泄漏,這里也可以使用智能指標,但是會相對麻煩,且更耗資源,本文暫不考慮,
懶漢模式(不考慮多執行緒,且不考慮記憶體泄漏,沒有回傳參考)
class singleton //實作單例模式的類
{
private:
singleton(){} //私有的建構式
static singleton* instance;
public:
static singleton* GetInstance()
{
if (instance== NULL) //判斷是否第一呼叫
instance= new singleton();
return instance;
}
};
下面兩個只呼叫了一次建構式
singleton* instance_1 = singleton::GetInstance();
singleton* instance_2 = singleton::GetInstance();
懶漢模式(多執行緒),增加一層instance== NULL判斷,以及Lock()
if (instance== NULL) //判斷是否第一呼叫
{
Lock(); //表示上鎖的函式
if (instance== NULL)
{
instance= new singleton();
}
UnLock() //解鎖函式
}
2. muduo::singleton
muduo里singleton主要做了以下動作
- 回傳&
- 使用atexit,在main函式回傳或者exit時,完成delete呼叫,完成堆疊清理
- 使用pthread_once設定PTHREAD_ONCE_INIT,只在第一次呼叫new,避免多執行緒競爭,多次new
其暴露了instance函式,因為其回傳參考,可以對T實體物件修改,但保證只有一個物件
2.1 singleton代碼
template<typename T>
class Singleton : noncopyable
{
public:
Singleton() = delete;
~Singleton() = delete;
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;
}
private:
static pthread_once_t ponce_;
static T* value_;
};
template<typename T>
pthread_once_t Singleton<T>::ponce_ = PTHREAD_ONCE_INIT;
template<typename T>
T* Singleton<T>::value_ = NULL;
2.2 muduo 測驗代碼
#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_;
};
class TestNoDestroy : muduo::noncopyable
{
public:
// Tag member for Singleton<T>
void no_destroy();
TestNoDestroy()
{
printf("tid=%d, constructing TestNoDestroy %p\n", muduo::CurrentThread::tid(), this);
}
~TestNoDestroy()
{
printf("tid=%d, destructing TestNoDestroy %p\n", muduo::CurrentThread::tid(), this);
}
};
void threadFunc()
{
printf("tid=%d, %p name=%s\n",
muduo::CurrentThread::tid(),
&muduo::Singleton<Test>::instance(),
muduo::Singleton<Test>::instance().name().c_str());
muduo::Singleton<Test>::instance().setName("only one, changed");
}
int main()
{
muduo::Singleton<Test>::instance().setName("only one");
muduo::Thread t1(threadFunc);
t1.start();
t1.join();
printf("tid=%d, %p name=%s\n",
muduo::CurrentThread::tid(),
&muduo::Singleton<Test>::instance(),
muduo::Singleton<Test>::instance().name().c_str());
muduo::Singleton<TestNoDestroy>::instance();
printf("with valgrind, you should see %zd-byte memory leak.\n", sizeof(TestNoDestroy));
}
2.3 ThreadLocalSingleton
分析其代碼:
muduo里ThreadLocalSingleton有如下特點
- 使用!t_value_判斷是否null
- 在pthread_key_create里,注冊destructor,執行緒結束時呼叫,完成堆疊清理
- 回傳&
- 保證只有一個ThreadLocal物件(否則在主執行緒中可以創建多個TSD物件,即多個key指標)
namespace muduo
{
template<typename T>
class ThreadLocalSingleton : noncopyable
{
public:
ThreadLocalSingleton() = delete;
~ThreadLocalSingleton() = delete;
static T& instance()
{
if (!t_value_)
{
t_value_ = new T();
deleter_.set(t_value_);
}
return *t_value_;
}
static T* pointer()
{
return t_value_;
}
private:
static void destructor(void* obj)
{
assert(obj == t_value_);
typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
T_must_be_complete_type dummy; (void) dummy;
delete t_value_;
t_value_ = 0;
}
class Deleter
{
public:
Deleter()
{
pthread_key_create(&pkey_, &ThreadLocalSingleton::destructor);
}
~Deleter()
{
pthread_key_delete(pkey_);
}
void set(T* newObj)
{
assert(pthread_getspecific(pkey_) == NULL);
pthread_setspecific(pkey_, newObj);
}
pthread_key_t pkey_;
};
static __thread T* t_value_;
static Deleter deleter_;
};
template<typename T>
__thread T* ThreadLocalSingleton<T>::t_value_ = 0;
template<typename T>
typename ThreadLocalSingleton<T>::Deleter ThreadLocalSingleton<T>::deleter_;
} // namespace muduo
2.2 pthread_once
pthread_once()呼叫會出現在多個執行緒中,init_routine()函式僅執行一次,究竟在哪個執行緒中執行是不定的,是由內核調度來決定
int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
使用初值為PTHREAD_ONCE_INIT的once_control變數保證init_routine()函式在本行程執行序列中僅執行一次
2.3 atexit的使用

值得注意的點在于atexit函式的呼叫,其在exit(3)或者main函式結束的時候,都會被呼叫,回顧下行程正常終止的五個情況,main函式回傳等同于exit(3),呼叫多次相當于進行函式的壓堆疊操作,先入后出,

文章鏈接
C++ 單例模式總結與剖析
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/231103.html
標籤:其他
上一篇:【白帽子學習筆記】CTF實踐
