祝福大家。 我正在尝试编写一个线程安全的单身人士以供将来使用。这是我能想到的最好的。任何人都可以发现任何问题吗?关键假设是静态初始化发生在动态初始化之前的单个线程中。 (这将用于商业项目和公司不使用提振:(生活将是一件轻而易举的事情:)否则:) PS:还没有检查这个编译,我的道歉。
/* There are two difficulties when implementing the singleton pattern: Problem (a): The "global variable instantiation fiasco". TODO: URL This is due to the unspecified order in which global variables are initialised. Static class members are equivalent to a global variable in C++ during initialisation. Problem (b): Multi-threading. Care must be taken to ensure that the mutex initialisation is handled properly with respect to problem (a). */ /* Things achieved, maybe: *) Portable *) Lazy creation. *) Safe from unspecified order of global variable initialisation. *) Thread-safe. *) Mutex is properly initialise when invoked during global variable intialisation: *) Effectively lock free in instance(). */ /************************************************************************************ Platform dependent mutex implementation */ class Mutex { public: void lock(); void unlock(); }; /************************************************************************************ Threadsafe singleton */ class Singleton { public: // Interface static Singleton* Instance(); private: // Static helper functions static Mutex* getMutex(); private: // Static members static Singleton* _pInstance; static Mutex* _pMutex; private: // Instance members bool* _pInstanceCreated; // This is here to convince myself that the compiler is not re-ordering instructions. private: // Singletons can't be coppied explicit Singleton(); ~Singleton() { } }; /************************************************************************************ We can't use a static class member variable to initialised the mutex due to the unspecified order of initialisation of global variables. Calling this from */ Mutex* Singleton::getMutex() { static Mutex* pMutex = 0; // alternatively: static Mutex* pMutex = new Mutex(); if( !pMutex ) { pMutex = new Mutex(); // Constructor initialises the mutex: eg. pthread_mutex_init( ... ) } return pMutex; } /************************************************************************************ This static member variable ensures that we call Singleton::getMutex() at least once before the main entry point of the program so that the mutex is always initialised before any threads are created. */ Mutex* Singleton::_pMutex = Singleton::getMutex(); /************************************************************************************ Keep track of the singleton object for possible deletion. */ Singleton* Singleton::_pInstance = Singleton::Instance(); /************************************************************************************ Read the comments in Singleton::Instance(). */ Singleton::Singleton( bool* pInstanceCreated ) { fprintf( stderr, "Constructor\n" ); _pInstanceCreated = pInstanceCreated; } /************************************************************************************ Read the comments in Singleton::Instance(). */ void Singleton::setInstanceCreated() { _pInstanceCreated = true; } /************************************************************************************ Fingers crossed. */ Singleton* Singleton::Instance() { /* 'instance' is initialised to zero the first time control flows over it. So avoids the unspecified order of global variable initialisation problem. */ static Singleton* instance = 0; /* When we do: instance = new Singleton( instanceCreated ); the compiler can reorder instructions and any way it wants as long as the observed behaviour is consistent to that of a single threaded environment ( assuming that no thread-safe compiler flags are specified). The following is thus not threadsafe: if( !instance ) { lock(); if( !instance ) { instance = new Singleton( instanceCreated ); } lock(); } Instead we use: static bool instanceCreated = false; as the initialisation indicator. */ static bool instanceCreated = false; /* Double check pattern with a slight swist. */ if( !instanceCreated ) { getMutex()->lock(); if( !instanceCreated ) { /* The ctor keeps a persistent reference to 'instanceCreated'. In order to convince our-selves of the correct order of initialisation (I think this is quite unecessary */ instance = new Singleton( instanceCreated ); /* Set the reference to 'instanceCreated' to true. Note that since setInstanceCreated() actually uses the non-static member variable: '_pInstanceCreated', I can't see the compiler taking the liberty to call Singleton's ctor AFTER the following call. (I don't know much about compiler optimisation, but I doubt that it will break up the ctor into two functions and call one part of it before the following call and the other part after. */ instance->setInstanceCreated(); /* The double check pattern should now work. */ } getMutex()->unlock(); } return instance; }
没有找到相关结果
已邀请:
5 个回复
icum
赞同来自:
内容太长未翻译
psed
赞同来自:
在代码的全局范围内:
这使您的实现不是懒惰的。大概你想在全局范围内将_pInstance设置为NULL,并在解锁互斥锁之前在Instance()中构造单例后分配给它。asaepe
赞同来自:
Meyers& amp; Alexandrescu,Singleton是特定目标:C++ and the Perils of Double-Checked Locking。这有点棘手的问题。
zcum
赞同来自:
如果您希望看到有关Singletons的深入讨论,关于其生命周期和线程安全问题的各种政策,我只能推荐一个很好的读物:Alexandrescu的“Modern C++ Design”。 实施在Loki的网站上展示,找到它here! 是的,它确实存在于单个头文件中。所以我真的鼓励你至少抓住文件并阅读它,更好的是阅读这本书以获得全面的反思。
tquod
赞同来自:
拥有一个在构造函数中什么也不做的非惰性单例通常会更好,然后在GetInstance中对一个分配任何昂贵资源的函数执行一次线程安全调用。你已经非懒惰地创建了一个Mutex,那么为什么不在你的Singleton对象中放入互斥量和某种Pimpl呢? 顺便说一句,这在Posix上更容易:
确实存在用于Windows和其他平台的pthread_once实现。