• 再谈Singleton


    再谈Singleton

    前些时候写了一篇关于Singleton模式的使用心得,发布在这个页面:

    http://blog.csdn.net/Li_Shugan1/archive/2010/08/09/5797873.aspx

    后来在实际应用的过程中又出现了一些问题,其间查了一些资料,主要是Modern C++ Design,解决了Singleton在C++中会出现的问题,其方案多有借鉴Modern C++ Design中的内容,但是对析构顺序的控制,是自己的想法,自我感觉要优于这本书的的,呵呵总的来说会到的问题主要三个:

    1. Singleton用那种方式创建。用类的静态指针,还是函数内的静态变量,或者直接用类的静态对象。
    2. 多个Singleton析构的顺序问题。
    3. 对多线程的支持。

    现在就一个一个慢慢道来。

    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》中的方案,这里主要解决析构的顺序问题。

    解决方案

    1. 由于Implementation1是没有创建的顺序问题的(当然如果你的两个Singleton创建时互相依赖,这连神仙都没办法,呵呵),我们可以继续延用这种方式。
    2. 对于析构的顺序,我们可以用一个容器来管理它,对于前面的例子中的两个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了,当然,希望大家能用一些更为复杂的例子来检测一下我这个算法^_^

  • 相关阅读:
    RBAC-实现不同用户拥有不同权限
    RBAC鉴权-通过聚合clusterrole实现集群权限控制
    kubernetes中Deployment和replicaset关系剖析
    常用php操作redis命令整理(四)SET类型
    LINUX 系统硬盘占满,找不到大文件,原来是进程问题
    c#使用System.Media.SoundPlayer播放资源文件中的wav文件
    Go语言中的并发安全和锁
    Go语言中的channel
    Go语言goroutine并发编程
    Sublime Text的这些快捷操作,你都会用吗
  • 原文地址:https://www.cnblogs.com/li_shugan/p/1841382.html
Copyright © 2020-2023  润新知