Design mode 12 single example mode

Keywords: Design Pattern

1. Motivation:

  • When there is only one or more, the performance will be reduced;
  • How to bypass conventional constructors and provide a mechanism to ensure that a class has only one instance?
  • This is the responsibility of the designer, not the user.

2. Single thread

class Singleton{
private:
Singleton();
Singleton(const Singleton& other);

public: 
    static Singleton* getInstance();
    static Singleton* m_instancer;
};

Singleton* Singleton::m_instancer=nullptr;
// Thread unsafe version
Singleton* Singleton::getInstance(){
    if(m_instance==nullptr){
        m_instance=new Singleton();
    }
    return m_instance;
}

2.1 self implemented cases

Supplement the usage of a static keyword

  • Modify the local variable of the function: it has a default value of 0 and runs only once. The memory is opened up at the beginning of the operation, and the memory is placed in the global;
  • Modify global functions and variables: can only be used in this source file
  • Modify the member variable of the class: the characteristics are similar to the first point. It does not depend on the existence of the class object and can be called directly.
  • Modify the member function of the class: this function can only call the static function or static variable of the class. It does not depend on the existence of the class and can be called directly.
  • Main usage of static:

1. static method

Static methods are generally called static methods. Because static methods can be accessed without relying on any object, there is no this for static methods, because they do not depend on any object. Since there are no objects, there is no this. Moreover, due to this feature, non static member variables and non static member methods of the class cannot be accessed in static methods, because non static member methods / variables must depend on specific objects to be called., However, it should be noted that although non static member methods and non static member variables cannot be accessed in static methods, static member methods / variables can be accessed in non static member methods.

2. static variable

Static variables are also called static variables. The difference between static variables and non static variables is that static variables are shared by all objects and have only one copy in memory. It will be initialized when and only when the class is first loaded.

Non static variables are owned by objects. They are initialized when creating objects. There are multiple copies, and the copies owned by each object do not affect each other. Static member variables are initialized in the defined order.

3. static code block

Static keyword also plays a key role in forming static code blocks to optimize program performance. Static blocks can be placed anywhere in the class, and there can be multiple static blocks in the class.

When the class is first loaded, each static block is executed in the order of static blocks, and only once. The reason why static block can be used to optimize program performance is that it is only executed once when the class is loaded.

//.h===================================
#pragma once
#include <iostream>
using namespace std;
class Singleton
{
private:
	Singleton();
	static int  m_instance;// static ensures that there is only one variable globally
public:
	static int getInstance();	
	~Singleton();
};
//.cpp=================================
#include "Singleton.h"
int Singleton::m_instance = 0;
Singleton::Singleton()
{}
int Singleton::getInstance() {
		return ++m_instance;
}
Singleton::~Singleton()
{}
//main.cpp=============================
#include "Singleton.h"
int main()
{
	int b = Singleton::getInstance();
	int c = Singleton::getInstance();
	cout << b << c;
	return 0;
}
//Output results; twelve

3. For multithreading

3.1

Thread safe version, but the cost of locking is too high. There is no problem with this method. Only for the case of high concurrency, each thread is locked, and other threads can only wait, which will lead to low efficiency (such as the online scenario of 100000 users)

//As can be seen below, every thread, regardless of M_ Whether instance has been assigned or not will lock indiscriminately, resulting in low efficiency
Singleton* Singleton::getInstance(){
    Lock lock;//Thread lock will lock the first thread, and other threads can only wait
    if(m_instance==nullptr){
        m_instance=new Singleton();
    }
    return m_instance;
}

3.2 the double check lock was once considered perfect, but now it can't be reused because the memory read-write reorder is not safe

  • reorder: for a shape such as a=new BClass(); Logically, the compiler executes such a new operation at a lower level in the following order: 1. Allocate memory - > 2. Call the constructor to construct class BClass and place the construction result in the allocated memory - > 3. Assign the allocated memory to a. However, in fact, the actual order of the underlying constructor may be 1 - > 3 - > 2, so memory is assigned to a before the constructor, which will cause subsequent threads to enter the function again due to m_instance's memory is no longer empty, but directly executes the return, and then there is no constructed class in the memory at this time, which causes an error.
Singleton * Singleton::getInstance(){
    if (m_instance==nullptr){
        Lock lock;
        if(m_instance==nullptr){
            m_instance=new Singleton();
        }
    }
    return m_instance;
}
  • Note that the class is checked twice in the double check lock.

3.3

  • For the problems in 3.2, the keyword volatile is given in Java and C# and can be used in actual use
  • For cpp, its form is as follows (the specific containers used need to be consulted later)
class Singleton{
private:
Singleton();
public:
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance(){
    Singleton* tmp=m_instance.load(std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_acquire);
    if(tmp==nullptr){
        tmp=new Singleton;
        std::atomic_thread_fence(std::memeory_order_release);
        m_instance.store(tmp,std::memory_order_releaxed);
    }
}
return tmp;
};

4. Summary

  • Singleton itself is a simple pattern, but some problems with multithreading make it a little more complex in practical use

5. Supplement: in Visual Studio zi

  • Source Files folder: put the source program.

  • Resource Files folder: put the Resource Files needed in the program, such as icons, dialog boxes, pictures, etc.

  • Source Files - *.c, *.cpp Header Files - *.h

  • Resource Files - *.ico, *.bmp, ...

Posted by forumsudhir on Fri, 01 Oct 2021 11:03:51 -0700