再谈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了,当然,希望大家能用一些更为复杂的例子来检测一下我这个算法^_^