再谈Singleton
再谈Singleton
前些时候写了一篇关于Singleton模式的使用心得,发布在这个页面:
http://blog.csdn.net/Li_Shugan1/archive/2010/08/09/5797873.aspx
后来在实际应用的过程中又出现了一些问题,其间查了一些资料,主要是Modern C++ Design,解决了Singleton在C++中会出现的问题,其方案多有借鉴Modern C++ Design中的内容,但是对析构顺序的控制,是自己的想法,自我感觉要优于这本书的的,呵呵总的来说会到的问题主要三个:
- Singleton用那种方式创建。用类的静态指针,还是函数内的静态变量,或者直接用类的静态对象。
- 多个Singleton析构的顺序问题。
- 对多线程的支持。
现在就一个一个慢慢道来。
Singleton的创建
3种创建方式的优缺点如下图所示.
这上面提到的Resource Leak是指诸如Network之类的资源没有释放。一开始我还以为会为内存泄漏,后来才知道静态指针是不会出现内存泄漏的,因为程序在退出时,操作系统负责清理所有的内存空间。
另外,这里提到的strong bugs,对 Implementation2来说可以来自两方面,Singleton的创建顺序和析构顺序。Implementation3主要是析构的顺序。
Singleton析构的顺序
这里只对Implementation3给个反例:
// Singleton.cpp : Defines the entry point for the console application. // #include <string> #include <iostream> using namespace std; class Log { public: static Log* GetInstance() { static Log oLog; return &oLog; } void Output(string strLog) { cout<<strLog<<(*m_pInt)<<endl; } private: Log():m_pInt(new int(3)) { } ~Log() {cout<<"~Log"<<endl; delete m_pInt; m_pInt = NULL; } int* m_pInt; }; class Context { public: static Context* GetInstance() { static Context oContext; return &oContext; } ~Context() { Log::GetInstance()->Output(__FUNCTION__); } void fun() { Log::GetInstance()->Output(__FUNCTION__); } private: Context(){} Context(const Context& context); }; int main(int argc, char* argv[]) { Context::GetInstance()->fun(); return 0; }
在这个反例中有两个Singleton: Log和Context,Context的fun和析构函数会调用Log来输出一些信息,结果程序Crash掉了,该程序的运行的序列图如下(其中画红框的部分是出问题的部分):
对多线程的支持
对多线程的支持大学可以参与《Modern C++ Design》中的方案,这里主要解决析构的顺序问题。
解决方案
- 由于Implementation1是没有创建的顺序问题的(当然如果你的两个Singleton创建时互相依赖,这连神仙都没办法,呵呵),我们可以继续延用这种方式。
- 对于析构的顺序,我们可以用一个容器来管理它,对于前面的例子中的两个Singleton: Log和Context,选释放Context,再释放Log,如果出现了循环依赖关系,我们要给出异常,并输出循环依赖环。
我的思路是这样的,(1)用一个特别的单例SingletonMgr来管理所有的单例,每个单例都必须register到SingletonMgr中,对会在析构函数中依赖别的Singleton的来说,它还要做一点事,那就是申明这种依赖,对上面这个反例,在实现Context的GetInstance方法时,要申明一下它会在在析构时依赖Log;
(2)在SingletonMgr析构时,按依赖关系对所有的Singleton的析构顺序排序,然后来调用各个Singleton的析构方法。
SingletonMgr的结构图如下:
每一个单例,在SingletonMgr中都有一SingletonItem与其对应,SingletonItem中存储该单例的名字,Destory方法和它所依赖的其他单例。
最为复杂的就是最后的排序算法啦,这里采用的 Depth First Traverse算法对其排序的,在排序的过程中会检测是否出现了依赖环,如果出现了依赖环,在打印了该依赖环后抛出异常。
算法框架如下图所示:
其中的Depth First Traverse步骤如图:
······················
在使用Depth First Traverse进行排序时,涉及到了一个SingletonItem的状态问题。SingletonItem一共有5种状态:
(1) E_UnSort: 在开始Sort之前,所有的SingletonItem都处于这个状态。
(2) E_Sorting:在处理一个SingletonItem的依赖时,会处于这个状态。
(3) E_Sorted:如果一个Item已经完全Sort好了,就会到这个状态。
(4) E_CannotSort:在处理完一个Item的依赖时,会被置为这个状态。
(5) E_SelfSorted:出现了依赖环。、
SingletonMgr的定义如下:
#ifndef SINGLETONMGR_HPP #define SINGLETONMGR_HPP #include <vector> #include <stack> #include <string> #include <list> using std::vector; using std::stack; using std::string; using std::list; typedef void (*SingletonReleaseFun)(void); class SingletonItem { public: enum ESortState { E_UnSort, E_Sorting, E_Sorted, E_CannotSort, E_SelfSorted }; SingletonItem(const string& strName,SingletonReleaseFun releaseFun); ~SingletonItem(); const string& Name()const{return m_strName;} void SetState(ESortState eState){m_eSortState = eState;} ESortState GetState(void){return m_eSortState;} void Push(SingletonItem* pDependencyItem); SingletonItem* Pop(void); bool IsEmpty(void){return m_stackDependencys.empty();} void SetReleaseFun(SingletonReleaseFun releaseFun){m_funRelease = releaseFun;} void Release(); private: SingletonItem(const SingletonItem&); string m_strName; SingletonReleaseFun m_funRelease; ESortState m_eSortState; stack<SingletonItem*> m_stackDependencys; }; class SingletonMgr { public: static SingletonMgr* GetInstance(); void Regesiter(const string& strName,SingletonReleaseFun pReleaseFun); void AddDependency(const string& strSingleton1,const string& strSingleton2); private: static void Release(void); void SortItems(void); void DepthFirstTraverse(SingletonItem *pItem,vector<SingletonItem*>& sortingItems); void OutputLoop(SingletonItem *pItem,vector<SingletonItem*>& sortingItems); void AdjustPosition(SingletonItem *pItem,vector<SingletonItem*>& sortingItems); size_t FindPosIn(vector<SingletonItem*>& items,SingletonItem* pItem); SingletonItem* GetSingleton(const string& pItem); SingletonMgr(); SingletonMgr(const SingletonMgr&); ~SingletonMgr(void); vector<SingletonItem*> m_vSingletons; list<SingletonItem*> m_listSortedSingletons; }; #endif
其实现如下:
#include "SingletonMgr.h" #include <iostream> #include <cassert> #include <algorithm> using std::cout; using std::endl; SingletonItem::SingletonItem(const string& strName,SingletonReleaseFun releaseFun): m_strName(strName) ,m_funRelease(releaseFun) ,m_eSortState(E_UnSort) { } SingletonItem::~SingletonItem() { } void SingletonItem::Release() { if(NULL != m_funRelease) { m_funRelease(); } } void SingletonItem::Push(SingletonItem* pItem) { m_stackDependencys.push(pItem); } SingletonItem* SingletonItem::Pop(void) { SingletonItem* pItem = m_stackDependencys.top(); m_stackDependencys.pop(); return pItem; } SingletonMgr* SingletonMgr::GetInstance() { static SingletonMgr oSingletonMgr; return &oSingletonMgr; } SingletonMgr::SingletonMgr(void) { } SingletonMgr::~SingletonMgr(void) { SortItems(); for(list<SingletonItem*>::iterator it = m_listSortedSingletons.begin(); it != m_listSortedSingletons.end(); ++it) { (*it)->Release(); } for(list<SingletonItem*>::iterator it = m_listSortedSingletons.begin(); it != m_listSortedSingletons.end(); ++it) { delete *it; } } void SingletonMgr::SortItems(void) { vector<SingletonItem*> sortingItems; for(size_t nIndex = 0; nIndex < m_vSingletons.size(); ++nIndex ) { SingletonItem* pItem = m_vSingletons[nIndex]; if(NULL != pItem && SingletonItem::E_Sorted != pItem->GetState()) { DepthFirstTraverse(pItem,sortingItems); } if(sortingItems.size() > 0) { m_listSortedSingletons.insert(m_listSortedSingletons.begin(), sortingItems.begin(),sortingItems.end()); for (size_t nPos = 0;nPos < sortingItems.size();++nPos) { SingletonItem* pItem = sortingItems[nPos]; if(NULL != pItem) { pItem->SetState(SingletonItem::E_Sorted); } } sortingItems.clear(); } } } void SingletonMgr::DepthFirstTraverse(SingletonItem *pItem,vector<SingletonItem*>& sortingItems) { switch(pItem->GetState()) { case SingletonItem::E_Sorted: break; case SingletonItem::E_Sorting: //encounter a loop. //output the loop OutputLoop(pItem,sortingItems); pItem->SetState(SingletonItem::E_CannotSort); assert(0); break; case SingletonItem::E_CannotSort://encounter another loop. //output the loop OutputLoop(pItem,sortingItems); assert(0); break; case SingletonItem::E_SelfSorted://need adjust AdjustPosition(pItem,sortingItems); break; default: { pItem->SetState(SingletonItem::E_Sorting); sortingItems.push_back(pItem); while(!pItem->IsEmpty()) { DepthFirstTraverse(pItem->Pop(),sortingItems); } pItem->SetState(SingletonItem::E_SelfSorted); } break; } } void SingletonMgr::OutputLoop(SingletonItem *pItem,vector<SingletonItem*>& sortingItems) { cout<<pItem->Name(); for(vector<SingletonItem*>::reverse_iterator it = sortingItems.rbegin(); (*it)->Name() != pItem->Name();++it) { cout<<"<--"<<(*it)->Name(); } cout<<"<--"<<pItem->Name()<<endl; } void SingletonMgr::AdjustPosition(SingletonItem *pItem,vector<SingletonItem*>& sortingItems) { size_t nItemPos = FindPosIn(sortingItems,pItem); vector<SingletonItem*>::iterator it4Item = find(sortingItems.begin(),sortingItems.end(),pItem); for (size_t nPos = nItemPos + 1; nPos < sortingItems.size();++nPos) { SingletonItem* pAdjustItem = sortingItems.at(nPos); if (NULL != pAdjustItem && SingletonItem::E_Sorting == pAdjustItem->GetState()) { sortingItems.erase(find(sortingItems.begin(),sortingItems.end(),pAdjustItem)); it4Item = sortingItems.insert(it4Item,pAdjustItem); ++it4Item; } } } size_t SingletonMgr::FindPosIn(vector<SingletonItem*>& items,SingletonItem* pItem) { for(size_t nPos = 0;nPos < items.size();++nPos) { if (pItem == items[nPos]) { return nPos; } } return -1; } void SingletonMgr::Regesiter(const string& strName,SingletonReleaseFun pReleaseFun) { SingletonItem* pItem = GetSingleton(strName); pItem->SetReleaseFun(pReleaseFun); } void SingletonMgr::AddDependency(const string& strSingleton1,const string& strSingleton2) { SingletonItem* pItem = GetSingleton(strSingleton1); pItem->Push(GetSingleton(strSingleton2)); } SingletonItem* SingletonMgr::GetSingleton(const string& strSingleton) { for(vector<SingletonItem*>::iterator it = m_vSingletons.begin(); it != m_vSingletons.end(); ++it) { if((*it)->Name() == strSingleton) { return *it; } } m_vSingletons.push_back(new SingletonItem(strSingleton,NULL)); return m_vSingletons.back(); }
为了方便实现一个Singleton,还定义了如下宏:
#ifndef _SINGLETONDEF_H #define _SINGLETONDEF_H #include "SingletonMgr.h" #define DECLARE_SINGLETON(Singleton) \ public: \ static Singleton* GetInstance(void); \ private: \ Singleton(const Singleton&) ; \ Singleton& operator=(const Singleton&) ; \ static void Release() ; \ static Singleton* g_pInstance; #define BEGIN_IMPLEMENT_SINGLETON(Singleton) \ Singleton *Singleton::g_pInstance = NULL; \ Singleton* Singleton::GetInstance() \ { \ SingletonMgr::GetInstance()->Regesiter(#Singleton,Singleton::Release); \ Singleton::g_pInstance = new #define END_IMPLEMENT_SINGLETON(Singleton) \ return Singleton::g_pInstance; \ } \ void Singleton::Release() \ { \ delete Singleton::g_pInstance; \ Singleton::g_pInstance = NULL; \ } #define DECLARE_DEPENDENCY(Singleton1,Singleton2) \ SingletonMgr::GetInstance()->AddDependency(#Singleton1,#Singleton2); #endif
有了这些,再实现上面的Context和Log如下:
// Singleton.cpp : Defines the entry point for the console application. // #include <string> #include <iostream> using namespace std; #include "SingletonDef.h" class Log { DECLARE_SINGLETON(Log); Log():m_pInt(new int(3)) { } public: void Output(string strLog) { cout<<strLog<<(*m_pInt)<<endl; } ~Log() {cout<<"~Log"<<endl; delete m_pInt; m_pInt = NULL; } int* m_pInt; }; BEGIN_IMPLEMENT_SINGLETON(Log) Log(); END_IMPLEMENT_SINGLETON(Log) class Context { DECLARE_SINGLETON(Context) Context(){} public: ~Context() { Log::GetInstance()->Output(__FUNCTION__); } void fun() { Log::GetInstance()->Output(__FUNCTION__); } }; BEGIN_IMPLEMENT_SINGLETON(Context) Context(); DECLARE_DEPENDENCY(Context,Log); END_IMPLEMENT_SINGLETON(Context) int main(int argc, char* argv[]) { Context::GetInstance()->fun(); return 0; }
再运行程序就不会Crash了,当然,希望大家能用一些更为复杂的例子来检测一下我这个算法^_^