Home >Backend Development >C#.Net Tutorial >Implementation of C# singleton pattern and examples of performance comparison
This article mainly introduces relevant information about the implementation and performance comparison of C# singleton mode. It introduces 6 implementation methods in detail. Friends in need can refer to the following
Introduction
A singleton refers to a class that can only have one instance (in C#, more accurately, it is a class that can only have one instance in each AppDomain. It is used in software engineering One of the most common modes. After the first user creates an instance of this class, subsequent users who need to use this class can only use the previously created instance and cannot create a new instance. A singleton is created when it is used for the first time. This article will introduce several singleton implementation methods in C# and analyze the thread safety and performance differences between them. There are many, but from the simplest implementation (non-lazy-loading, non-thread-safe, and inefficient) to a lazily-loadable, thread-safe, and efficient implementation, they all have some basic things in common:
One non-thread safety
//Bad code! Do not use!
public sealed class Singleton
{
private static Singleton instance = null;
private Singleton()
{
}
public static Singleton instance
{
get
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
This method is not thread-safe Yes, there will be two threads executing if (instance == null) at the same time and creating two different instances. The one created later will replace the newly created one, causing the previously obtained reference to be empty
#. ##Second Simple Thread Safety Implementation
public sealed class Singleton { private static Singleton instance = null; private static readonly object padlock = new object(); Singleton() { } public static Singleton Instance { get { lock (padlock) { if (instance == null) { instance = new Singleton(); } return instance; } } } }Compared with implementation one, this version adds a lock on the instance. The padlock must be locked before calling the instance, thus avoiding Due to the thread conflict in Implementation 1, this implementation will only create one instance from beginning to end. However, since the lock is used every time the instance is called, and the cost of calling the lock is large, this implementation will have a certain performance loss #. ## Note that here we use a new private object instance padlock to implement the lock operation, instead of directly locking the Singleton. There are potential risks in directly locking the type, because this type is public, so in theory. It will be called in any code, and locking it directly will cause performance problems and even deadlocks.
Note: In C#, the same thread can lock an object multiple times. , but if different threads are locked at the same time, thread waiting may occur, or serious deadlock may occur. Therefore, when we use lock, try to lock private variables in the class, so as to avoid the above situation.
Triple double verification thread safety implementationpublic sealed calss Singleton { private static Singleton instance = null; private static readonly object padlock = new object(); Singleton() { } public static Singleton Instance { get { if (instance == null) { lock (padlock) { if (instance == null) { instance = new Singleton(); } } } return instance; } } }
While ensuring thread safety, this implementation also avoids the lock operation every time Instance is called, which will save money A certain amount of time. However, this implementation also has its disadvantages:
1 does not work in Java. (The specific reasons can be found in the original text, I don’t understand much here)
2 Programmers can easily make mistakes when implementing it themselves. If you make your own modifications to the code in this mode, be very careful because the logic of double check is relatively complex and it is easy to make mistakes due to poor thinking.Four thread-safe implementations without locks
public sealed class Singleton { //在Singleton第一次被调用时会执行instance的初始化 private static readonly Singleton instance = new Singleton(); //Explicit static consturctor to tell C# compiler //not to mark type as beforefieldinit static Singleton() { } private Singleton() { } public static Singleton Instance { get { return instance; } } }
This implementation is very simple and does not use locks, but it is still thread-safe. A static, readonly Singleton instance is used here. It will create a new instance when the Singleton is called for the first time. The thread safety guarantee when creating the new instance is directly controlled by .NET. We can think of it as an atomic operation. And it will only be created once in an AppDomaing.
This implementation also has some shortcomings:1The timing of instance being created is unknown, and any call to Singleton will create the instance in advance2The cyclic call of the static constructor . If there are two classes, A and B, and B is called in the static constructor of A, and A is called in the static constructor of B, these two will form a circular call, which will seriously cause the program to crash. 3 We need to manually add the static constructor of Singleton to ensure that the Singleton type will not be automatically added with the beforefieldinit Attribute to ensure that the instance will be created when Singleton is called for the first time.
4 The readonly attribute cannot be changed at runtime. If we need to dispose the instance and recreate a new instance when the program is running, this implementation method cannot be satisfied.
Five fully lazy loading implementation (fully lazy instantiation)
public sealed class Singleton { private Singleton() { } public static Singleton Instance { get { return Nested.instance; } } private class Nested { // Explicit static constructor to tell C# compiler // not to mark type as beforefieldinit static Nested() { } internal static readonly Singleton instance = new Singleton(); } }
Implementation five is a wrapper for implementation four. It ensures that instance will only be called in the get method of Instance and will only be initialized before the first call. It is a version of implementation 4 that ensures lazy loading.
6 Use .NET4's Lazy8742468051c85b06f0a0af9e3e506b5c typepublic sealed class Singleton { private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton()); public static Singleton Instance { get { return lazy.Value; } } private Singleton() { } }
.NET4 or above version supports Lazy8742468051c85b06f0a0af9e3e506b5c to implement lazy loading, which uses the simplest The code ensures the thread safety and lazy loading characteristics of the singleton. Performance difference In the previous implementation, we emphasized the thread safety and lazy loading of the code. However, in actual use, if the initialization of your singleton class is not a time-consuming operation or the initialization sequence does not cause bugs, delayed initialization is a dispensable feature because the time taken by the initialization is negligible. of. In actual usage scenarios, if your singleton instance will be called frequently (such as in a loop), then the performance consumption caused by ensuring thread safety is more worthy of attention. In order to compare the performance of these implementations, I did a small test, looping through the singletons in these implementations 900 million times, calling the instance method each time to perform a count++ operation, and every one million Output once, the running environment is Visual Studio for Mac on MBP. The results are as follows: The test method is not rigorous, but it can still be seen that method two has The need to call lock is the most time-consuming, almost three times as long as the others. Ranked second is the implementation using the .NET Lazy type, which is about one-half more than the others. The remaining four have no obvious differences. Summary Generally speaking, the various singleton implementation methods mentioned above are not very different under today's computer performance, unless you need a particularly large amount of concurrency. Only when you call instance will you need to consider lock performance issues. For ordinary developers, it is good enough to use method 2 or method 6 to implement singletons. Methods 4 and 5 require a good understanding of the C# running process and implementation They require certain skills, and the time they save is still limited. Quote Most of this article is translated from Implementing the Singleton Pattern in C#, with some of my own understanding added. This is what I saw when I searched for static readonly field initializer vs static constructor initialization. I would like to express my thanks to the two authors here.
Thread safety
Lazy loading
Test running time (ms)
Realize one
No
Yes
15532
Realization Two
Yes
Yes
45803
Realization Three
Yes is
15953
realizes four
is
incomplete
14572
realize five
是
是
14295
realize six
是
是
22875
The above is the detailed content of Implementation of C# singleton pattern and examples of performance comparison. For more information, please follow other related articles on the PHP Chinese website!