Modern C++ Design 笔记 第六章 Implementing Singletons(2)

说来惭愧,大学没有好好学,第一次听到这个所谓Singleton的时候是在一个校园招聘会上。大致的题目就是要写一个Singleton,答案么大概也可以预计,就是什么都不知道, 呵呵。时过境迁,也写过了一些这样的东西,现在回过头来,还有很有意义的。不搞的特别另类,还是循着书中介绍的顺序来看看吧。
  1. // Header file Singleton.h
  2. class Singleton
  3. {
  4. public:
  5.       static Singleton* Instance() // Unique point of access
  6.       {
  7.          return &instance_;
  8.       }
  9.       int DoSomething();
  10. private:
  11.       Singleton();
  12.       Singleton(const Singleton&);
  13.       Singleton& operator=(const Singleton&);
  14.       ~Singleton();
  15. };
  16. // Implementation file Singleton.cpp
  17. Singleton Singleton::instance_;

大致看上去这个东西还是比较怪异的,怪异在什么地方呢?就是这个static的instance_是一个object而不是一个指针。这个带来什么问题呢?就是这个变量的初始化时间不是程序员决定的而是编译器决定的。这样的话一些其他的变量如果对之有依赖的话,就会引发一些没有定义的行为。比如说这里在其他文件中( 注意其他文件中)有另一个全局变量.
  1. int globala = Singleton::Instance()->DoSomething();
那这个globala在创建的时候就是未定义的。 当然这种行为只是未定义而不是一定是错的,就我用MS的编译器测试来看,instance_是可以被正确的初始化的,同时类似的行为用一个全部变量来初始化另一全局变量貌似问题也不大。(而其编译器都没有任何的warning!!!,但是不得不提一句,在C里面你要是想做这样的事情恐怕就是不罩的因为所谓要求右值是常量的限制 ),在网上搜索了一下看到了一些的论述,大致的意思和书中类似,比如下面这个URL http://stevewetherill.com/2008/10/25/c-static-global-initialization/ 但是实际用这里的例子跑一便之后发现答案还是正确的,应该说我们尽力避免这样初始化依赖,但是同时我们相信我们的编译器也会越来越聪明:)

废话说了很多,回来原来的实现上来。所以原则上我们实现的模型归结为2个。
1) 指针模型
  1. static Singleton* pInstance_; // The one and only instance
  2. static Singleton* Instance() // Unique point of access
  3. {
  4. if (!pInstance_)
  5. pInstance_ = new Singleton;
  6. return pInstance_;
  7. }
2) 静态局部变量模型
  1. static Singleton* Instance() // Unique point of access
  2. {
  3.       static Singleton instance;
  4.       return &instance;   
  5. }
有了这两个模型之后,进一步我们要探索的是2个问题,一个是多线程的实际环境,另一个就是singleton的生命周期。
就生命周期我们有很深的体会,确实在很多应用程序开发的过程中碰到了诸如实例删除顺序依赖以及重新调用了已删除了实例的实例。这里面也给出了精彩的解法。
第一个就是所谓的Phoenix Singleton。援引原文的描述: Just as the legendary Phoenix bird rises repeatedly from its own ashes, the Phoenix singleton is able to rise again after it has been destroyed.(这说的不是一辉吗:))这个Phoenix在被删除后重新使用时重新创建实例,然后调用atexit,希望系统帮助来删除实例。
  1. void Singleton::OnDeadReference()
  2. {
  3. // Obtain the shell of the destroyed singleton
  4. Create();
  5. // Now pInstance_ points to the "ashes" of the singleton
  6. // - the raw memory that the singleton was seated in.
  7. // Create a new singleton at that address
  8. new(pInstance_) Singleton;
  9. // Queue this new object's destruction
  10. atexit(KillPhoenixSingleton);
  11. // Reset destroyed_ because we're back in business
  12. destroyed_ = false;
  13. }
  14. void Singleton::KillPhoenixSingleton()
  15. {
  16. // Make all ashes again
  17. // - call the destructor by hand.
  18. // It will set pInstance_ to zero and destroyed_ to true
  19. pInstance_->~Singleton();
  20. }
实现虽然在这里了,但是我依然很质疑他的原拆原建模型,不能肯定这个原先static的指针指向的内存会不会被系统回收掉,最近比较忙就不测试了,有机会一定好好搞一搞。
第二个生命周期的policy就是Longevity,不知道中文版把他翻译成什么总之很难翻。基本的思路就是每个singleton甚至全局变量都给上一个生命值,那在删除的时候就会依次进行,避免了顺序上的问题,同时也避免使用诸如dependency类一样的高耦合的模板,在这里我们就不展开了。

最后要说的就是多线程了,以前看过一个scott mayer的关于singleton多线程的讨论,这个模型的演变和作者的顺序如出一辙。
第一步就是没有多线程保护的版本,就是
  1. Singleton& Singleton::Instance()
  2. {
  3. if (!pInstance_) // 1
  4. {
  5. pInstance_ = new Singleton; // 2
  6. }
  7. return *pInstance_; // 3
  8. }
第二步那高级了一点,进了函数之后就有一个lock的变量。
  1. Singleton& Singleton::Instance()
  2. {
  3. // mutex_ is a mutex object
  4. // Lock manages the mutex
  5. Lock guard(mutex_);
  6. if (!pInstance_)
  7. {
  8. pInstance_ = new Singleton;
  9. }
  10. return *pInstance_;
  11. }
但是这样带来的问题是效率不高,实际上guard只需要看护的就是一种情况,就是最初的创建singleton实例的这个调用语句。在已经创建好的环境下这样的guard已经没有太多的意义了。
所有这才有了第三步。
  1. Singleton& Singleton::Instance()
  2. {
  3. if (!pInstance_) // 1
  4. // 2
  5. Guard myGuard(lock_); // 3
  6. if (!pInstance_) // 4
  7. {
  8. pInstance_ = new Singleton;
  9. }
  10. }
  11. return *pInstance_;
  12. }
把这个guard延后到了pInstance确实没有创建的时候,在效率上获得了不小的提高。(其实自己写个singleton哪有这么复杂啊,呵呵)

综上所述,我们最后又用到第一章的理论,用一个SingletonHolder把前面的那些模型用policy实现出来,这个SingletonHolder的定义如下:
  1.     template
  2.     <
  3.         typename T,
  4.         template <classclass CreationPolicy = CreateUsingNew,
  5.         template <classclass LifetimePolicy = DefaultLifetime,
  6.         template <classclassclass ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL,
  7.         class MutexPolicy = LOKI_DEFAULT_MUTEX
  8.     >
  9.     class SingletonHolder
  10.     {
  11.     public:
  12.         ///  Type of the singleton object
  13.         typedef T ObjectType;
  14.         ///  Returns a reference to singleton object
  15.         static T& Instance();
  16.         
  17.     private:
  18.         // Helpers
  19.         static void MakeInstance();
  20.         static void LOKI_C_CALLING_CONVENTION_QUALIFIER DestroySingleton();
  21.         
  22.         // Protection
  23.         SingletonHolder();
  24.         
  25.         // Data
  26.         typedef typename ThreadingModel<T*,MutexPolicy>::VolatileType PtrInstanceType;
  27.         static PtrInstanceType pInstance_;
  28.         static bool destroyed_;
  29.     };
perfect!!





你可能感兴趣的:(多线程,C++,object,测试,编译器,程序开发)