Write a Windows daemon (2) Singleton, windows daemon
Write a daemon on Windows (2) Singleton
The implementation of the log class in the previous article includes the following:
class Singleton<CLoggerImpl>
You can see what the name means-Singleton. This is a singleton template class.
A process usually has only one log instance, which is suitable for the singleton mode. So how to design a good Singleton?
We usually see the following implementation on the Internet:
class SingletonAA{public: static SingletonAA& get_instance_ref() { static SingletonAA _inst; return _inst; }private: SingletonAA() { //... } ~SingletonAA() { //... }};
When the get_instance_ref function was called for the first time, a static instance was constructed. I used this method all the time. Later I saw some articles about Singleton to know that this implementation was problematic: the C ++ standard before C ++ 11 does not specify the thread security for local static variable initialization. That is to say, this static variable may be initialized by two threads at the same time or a part of the initialization of one thread, and the other thread may start to initialize from the beginning.
To ensure thread security, some users may use this method:
class SingletonAA{public: static SingletonAA& get_instance_ref() { if (NULL == p_) { p_ = new SingletonAA(); } return *p_; }private: SingletonAA() { //... } ~SingletonAA() { //... }private: static SingletonAA *p_;};SingletonAA *SingletonAA::p_ = NULL;
Use Pointer to judge whether the pointer is null before new. However, there is still a problem: both threads may think that the pointer is null, and then they all go to new.
So with Double-Checked Locking Pattern (DCLP ):
static SingletonAA& get_instance_ref(){ if (NULL == p_) // 1st check { scoped_lock lock; if (NULL == p_) // 2nd check { p_ = new SingletonAA(); } } return *p_;}
Make two judgments. Because new is in the lock, you don't have to worry about multiple threads simultaneously new; after entering the lock, another judgment is made, ensure that no other thread is in the gap between the "first judgment" and "Lock" actions.
This is basically thread-safe.
But -- well, there is "but" -- this is still wrong in C ++. The problem lies in this sentence:
p_ = new SingletonAA();
Don't look at this as a code. There are actually three actions:
1. Allocate sizeof (SingletonAA) memory size
2. Construct a SingletonAA object in the allocated memory.
3. point p _ to the memory.
C ++ does not specify the execution sequence of these three steps, but you can also think that the first step must be executed first. "Practice" (from the DCLP reference at the end of this article) found that the compiler may swap the execution sequence of step 2 and step 3. Let's imagine the execution of step 3 before Step 2:
Allocate memory
Pointer assignment
Constructor
If the time slice of this thread is used up after the "pointer assignment", another thread happens to go to the "1st check" and finds that the pointer is not empty, then you can start to use this uninitialized object!
Maybe you may vomit, why should the compiler put "pointer assignment" before "constructor object"? "too na has ve, you don't know in my world. For more information, see the DCLP references.
In this case
p_ = new SingletonAA();
The code and the judgment condition can be separated, so do the following:
class SingletonAA{public: static SingletonAA& get_instance_ref() { if (!init_flag_) // 1st check { scoped_lock lock; if (!init_flag_) // 2nd check { p_ = new SingletonAA(); init_flag_ = true; } } return *p_; }private: //...private: static SingletonAA *p_; static bool init_flag_;};SingletonAA *SingletonAA::p_ = NULL;bool SingletonAA::init_flag_ = false;
Bool is a byte in vc2008, and only one assembly instruction is required for reading and writing (I have not tried vc and g ++ in other versions). It is an atomic operation and thread security is not considered.
It seems like this is done, but -- well, there is another one, but -- In the get_instance_ref function, the compilerPossibleThe init_flag _ reading will be optimized: the compiler finds that you have not assigned a value to init_flag _ within the function, so in fact itPossibleRead from the variable address only once and put it in the register. "2nd check" is obtained from the register instead of from the variable address, the results of these two checks will always be the same.
Of course, we have a way to solve the problem of Compiler Optimization. You must know that there is a keyword volatile. Its function is to tell the compiler that the variable will change at any time, please do not cache its value. Every time it is retrieved from the address, we need to declare init_flag _ as volatile:
static bool volatile init_flag_;
Initialize as follows:
bool volatile SingletonAA::init_flag_ = false;
It looks like this, but -- this time it's not needed, but it's another taste -- this is still a problem. For more information, see the DCLP references (here is my todo ).
In short, the single instance cannot be handled using DCLP.
What should we do?
Now it's time to sacrifice the kill call_once.
Call_once, as the name implies, is called only once. A parameter for this item is a function object. Its function is to ensure that the function object you pass to it is executed only once. If there is another thread in the execution process, you must wait for the execution to complete and use the execution result as your own result.
Boost: call_once, C ++ 11 has incorporated it into the standard std: call_once.
My ultimate solution uses it:
class SingletonAA{public: static inline SingletonAA& get_instance_ref() { boost::call_once(once_, init); return *p_; }private: //... static void init() { p_ = new SingletonAA(); }private: static SingletonAA *p_; static boost::once_flag once_;};SingletonAA *SingletonAA::p_ = NULL;boost::once_flag SingletonAA::once_;
If you are interested, you can see how boost: call_once is implemented (here is another todo ).
There is another problem: Resource release.
We have a new object, but there is no delete.
One way is to explicitly provide a destroy function, so that the destruction must be guaranteed by the caller. The other way is to use smart pointers.
As Singleton may be used in many parts of the project, I made the implementation of Singleton into a base class for the sake of universality, the subclass does not have to write code such as get_instance_ref:
Show you my code:
template<typename Type>class Singleton : public boost::noncopyable{public: static Type& get_instance_ref() { boost::call_once(once_, init); return *(p_.get()); }protected: Singleton(){} ~Singleton(){}private: static void init() { p_.reset(new Type()); }private: typedef boost::shared_ptr<Type> InstancePtr; static InstancePtr p_; static boost::once_flag once_;};template<typename Type>boost::once_flag Singleton<Type>::once_;template<typename Type>typename Singleton<Type>::InstancePtr Singleton<Type>::p_;
For usage instructions, see the source code.
Source code: https://git.oschina.net/mkdym/DaemonSvc.git (main) & https://github.com/mkdym/DaemonSvc.git (to improve the Force Grid ).
Reference link:
1. http://silviuardelean.ro/2012/06/05/few-singleton-approaches/ please bring your own ladder
2. DCLP: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
Sunday, January 1, October 25, 2015