1. Introduction
This article describes the use of implementing singleton patterns, including the definition of singleton patterns, and examples of using singleton patterns to implement singleton patterns, while also comparing the implementation of other singleton patterns. Finally, we end with an example using an implementation of a singleton pattern in an open source framework.
2. Basic implementation
2.1 Singleton pattern definition
Singleton pattern is a creative design pattern that ensures that a class has only one instance and provides a global access point to access this instance. Throughout the application, all accesses to this class will return the same instance object.
2.2 Implementing singleton mode
Here is a simple example code usingImplement singleton mode:
package singleton import "sync" type singleton struct { // The state of a singleton object } var ( instance *singleton once ) func GetInstance() *singleton { (func() { instance = &singleton{} // Initialize the state of a singleton object }) return instance }
In the above example code, we define asingleton
The structure represents the state of a singleton object and then uses its instance as a package-level variableinstance
and use oneonce
Variables to ensureGetInstance
The function is executed only once.
existGetInstance
In the function, we useMethod to execute an initialized singleton object. because
The method is implemented based on atomic operations, so concurrency security can be ensured, even if multiple coroutines are called simultaneously.
GetInstance
Functions will eventually create only one object.
2.3 Other ways to implement singleton mode
2.3.1 Assign values when defining global variables to implement singleton mode
In Go, global variables are automatically initialized when the program starts. Therefore, if you assign a value to a global variable when defining it, the creation of the object will also be completed at the start of the program. This can be used to implement singleton mode. Here is a sample code:
type MySingleton struct { // Field definition} var mySingletonInstance = &MySingleton{ // Initialize the field} func GetMySingletonInstance() *MySingleton { return mySingletonInstance }
In the above code, we define a global variablemySingletonInstance
And assign values at definition time, so that objects are created and initialized at program startup. existGetMySingletonInstance
In the function, we can directly return global variablesmySingletonInstance
, thereby implementing singleton mode.
2.3.2 The init function implements singleton mode
In Go, we can useinit
Functions to implement singleton pattern.init
Functions are functions that are automatically executed when the package is loaded, so we can create and initialize singleton objects in it, ensuring that the object is created at the start of the program. Here is a sample code:
package main type MySingleton struct { // Field definition} var mySingletonInstance *MySingleton func init() { mySingletonInstance = &MySingleton{ // Initialize the field } } func GetMySingletonInstance() *MySingleton { return mySingletonInstance }
In the above code, we define a package-level global variablemySingletonInstance
, and ininit
The object is created and initialized in the function. existGetMySingletonInstance
In the function, we directly return the global variable to implement the singleton pattern.
2.3.3 Implementing singleton mode using mutex locks
In Go, you can use only one mutex to implement singleton mode. Here is a demonstration of a simple code:
var instance *MySingleton var mu func GetMySingletonInstance() *MySingleton { () defer () if instance == nil { instance = &MySingleton{ // Initialize the field } } return instance }
In the above code, we use a global variableinstance
To store singleton objects and use a mutex lockmu
to ensure the creation and initialization of objects. Specifically, we areGetMySingletonInstance
First add lock in the function, then judgeinstance
Whether it has been created, and if it has not been created, create and initialize the object. Finally, we release the lock and return the singleton object.
It should be noted that using a mutex to implement singleton mode in case of high concurrency may cause performance problems. Because when a goroutine acquires a lock and creates an object, other goroutines need to wait, which may cause the program to slow down.
2.4 Advantages of using singleton mode
Relative toinit
Methods and implementations of the assignment singleton pattern using global variables,Implementing singleton mode allows for delayed initialization, that is, creation and initialization are only done when a singleton object is used for the first time. This can avoid the creation and initialization of objects at the start of the program and the possible waste of resources.
Compared with using mutex to implement singleton mode,The advantage of implementing a singleton mode is that it is simpler and more efficient. Provides a simple interface, which only needs to pass an initialization function. Compared with the implementation method of mutex locks, it requires manual processing of locks, judgments and other operations, which is more convenient to use. Moreover, using mutex to implement singleton mode requires locking and unlocking operations every time a singleton object is accessed, which adds additional overhead. and use
Implementing singleton mode can avoid these overheads, and only requires an initialization operation when accessing a singleton object for the first time.
But it's notIt is suitable for all scenarios, and this requires specific analysis of specific situations. The following description
and
init
Method, in which scenariosinit
Better, in which scenarios to usebetter.
2.5 and init method applicable scenarios
forinit
Implementing a singleton is more suitable for scenarios where variables need to be initialized when the program starts. becauseinit
Functions are executed before the program runs, which ensures that variables have been initialized when the program runs.
For certain objects that need to be initialized late, the object is created and will not be used immediately, or may not be used, such as creating a database connection pool. Use this timeIt's very suitable. It ensures that the object is only initialized once and will be created when needed, avoiding unnecessary waste of resources.
3. Use of singleton mode in gin
3.1 Background
Here we need to introduce it first,
It is the core component of the Gin framework, which is responsible for handling HTTP requests and routing requests to the corresponding processor. The processor can be a middleware, a controller, or a process HTTP response, etc. Each
Instances all have their own routing tables, middleware stacks and other configuration items. By calling their methods, you can register routes, middleware, processing functions, etc.
An HTTP server will only have a corresponding oneinstance, which saves routing mapping rules and other contents.
In order to simplify the use of developers' Gin framework, no user creation is requiredInstances can complete route registration and other operations, improve the readability and maintainability of the code, and avoid the occurrence of duplicate code. Here, for some commonly used functions, some functions are extracted for use, and the function signature is as follows:
// ginS/ // Load HTML template filefunc LoadHTMLGlob(pattern string) {} // Register POST request handlerfunc POST(relativePath string, handlers ...) {} // Register the GET request handlerfunc GET(relativePath string, handlers ...) {} // Start an HTTP serverfunc Run(addr ...string) (err error) {} // etc...
These functions need to be implemented next.
3.2 Specific implementation
First, start from the use, here we use the POST method/GET method to register the request processor, and then use the Run method to start the server:
func main() { // Register the corresponding processor of the url POST("/login", func(c *) {}) // Register the corresponding processor of the url GET("/hello", func(c *) {}) // Start the service Run(":8080") }
The effect we want here should be to call the Run method and start the service./login
The path sends a request, and the corresponding processor we registered should be executed at this time./hello
The same applies to path sending requests.
Therefore, here POST method, GET method, and Run method should all be the sameDo it, rather than using its own
Instance, or create one for each call
Example. Only in this way can we achieve the expected results.
So, we need to implement a method to obtainInstance, each time this method is called, the same instance is obtained, which is actually the definition of a singleton. Then, the POST method, GET method or Run method is called to obtain
Instance, then call the instance to call the corresponding method to complete the registration of the url processor or the startup of the service. This will ensure that the same one is used
An example. The specific implementation is as follows:
// ginS/ import ( "/gin-gonic/gin" ) var once var internalEngine * func engine() * { (func() { internalEngine = () }) return internalEngine } // POST is a shortcut for ("POST", path, handle) func POST(relativePath string, handlers ...) { return engine().POST(relativePath, handlers...) } // GET is a shortcut for ("GET", path, handle) func GET(relativePath string, handlers ...) { return engine().GET(relativePath, handlers...) }
hereengine()
Method usedImplement singleton mode to ensure that the same method is returned every time it is called
Example. Then the POST/GET/Run method is obtained through this method
Instance, then call the corresponding methods in the instance to complete the corresponding functions, so as to achieve the effect that POST/GET/Run and other methods are operated using the same instance.
3.3 Benefits of implementing singletons
The purpose to be achieved here is actually the function extracted from GET/POST/Run, etc., using the sameExample.
To achieve this, we can actually define itinternalEngine
When a variable is assigned, it is assigned; orinit
Function completesinternalEngine
Assigning variables is actually OK.
However, the functions we extracted are not necessarily used by the user, and are initialized when defined orinit
The assignment of variables is completed in the method. If the user does not use them, it will be created.The instance has no practical purpose and causes unnecessary waste of resources.
The engine method usesImplemented
internalEngin
Delay initialization is only when it is actually usedinternalEngine
It will be initialized only when it is , avoiding unnecessary waste of resources.
This actually confirms what we said aboveFor applicable scenarios, for singleton objects that will not be used immediately, you can use it at this time
To achieve it.
4. Summary
Singleton pattern is a commonly used design pattern that ensures that there is only one instance of a class. In singleton mode, singletons are often implemented using mutex locks or variable assignments. However, singletons can be implemented more easily using, while also avoiding unnecessary waste of resources. Of course, no implementation is suitable for all scenarios, and we need to analyze it according to the specific scenario.
The above is the detailed explanation of Go to achieve efficient singleton mode. For more information about singleton mode, please pay attention to my other related articles!