In-depth research on Singleton Mode

Source: Internet
Author: User

(1): Starting from static class and static Constructor

Static class and static Constructor (or static constructor) are the basic knowledge of the singleton mode.
I. Static class
C #2.0 provides static classes. In 1.x, we need to use the following code to implement static classes.

1.0
Public sealed class Class1
{
Private Class1 (){}
}

In C #2.0, we can use static class to solve this problem more elegantly.
Public static class Class1
{
}

After decompiling, we will understand its implementation mechanism.

. Class public abstract auto ansi sealed beforefieldinit Program
Extends object
{
}

The original compiler declared this class as abstract sealed, which naturally cannot be inherited and instantiated.
From this decompilation code, we can know a very important piece of information: the static class mechanism is only for processing during compilation, and the CLR
The runtime mechanism has not changed because of the emergence of static classes.
However, the C # compiler does not allow us to declare an abstract sealed type directly in the Code. The following Code cannot be compiled.

Public abstract sealed class Class1
{
}

Static class restrictions

1. The static class cannot have an instance constructor.
2. The static class cannot have any instance members.
3. abstract and sealed modifiers cannot be used for static classes.
4. Static classes are inherited from System. Object by default, and no other base classes can be explicitly specified.
5. No interface implementation can be specified for static classes.
6. Members of the static class cannot have the protected or protected internal access protection modifier.
The static classes on MSDN are described as follows:
When a class is declared as static, it indicates that it only contains static members. You cannot use the new keyword to create a static class instance. When a static class loads a program or namespace containing the class, it is automatically loaded by the. NET Framework Common Language Runtime Library (CLR.
Static classes are used to include methods that are not associated with specific objects. For example, it is common to create a group of methods that do not operate on instance data and are not associated with a specific object in the code. You should use static classes to include those methods.
The main functions of static classes are as follows:

They only contain static members.

They cannot be instantiated.

They are sealed.

They cannot contain instance constructors.
For more information about static classes, see
Http://msdn2.microsoft.com/zh-cn/library/79b3xss3 (VS.80). aspx
Http://www.codeproject.com/useritems/C__20_static_class.asp
Sometimes we need to put a bunch of static methods in a class, and this class does not have any instance members, such as System. web. httpUtility and. the Math library in the Net Framework is such an example. Because there are no instance members, inheritance or instantiation have no meaning, so static classes are still useful.

2. Static Constructor (or static constructor ):
The static constructor can be used in C # to initialize class data in the same way as the instance constructor used to initialize instance data. There are some differences between static constructor and instance constructor rules. Unlike the instance constructor, the static constructor cannot be overloaded. Therefore, the available static constructor has only one default non-parameter static Constructor (or a keyword such as private and public cannot be added ). Static constructors cannot be explicitly called or inherited from the derived class, but can be called when a base class type is created.
C # principles for using static constructor:
1. The static constructor is called before the class instance is created. Therefore, it is called before all instance constructor instances.
2. The static constructor is called before the first instance of the class is created.
3. The static constructor is called before a static field is referenced.
4. Static constructors can only be used to initialize static fields.
5. Add the static keyword. You cannot add access modifiers because static constructors are private.
6. The static constructor of the class can be executed at most once in the given application domain (AppDomain): The static constructor is activated only when the class instance is created or any static member of the referenced class is used.
7. Static constructors cannot be inherited and cannot be called directly.
8. If the class contains the Main method used to start execution, the static constructor of the class will be executed before the Main method is called.
9. Any static field with an initial value setting item, the initial values must be executed in the text order before executing the static constructor of this class.

Class Test
{
Static Test ()
{
Console. WriteLine ("");
}

Public Test ()
{
Console. WriteLine ("B ");
}
}
Class Test1: Test
{
Public Test1 ()
{
Console. WriteLine ("c ");
}
}
During instantiation

Test t = new Test (); // first, call the static constructor of Test to output a, and then call the instance constructor of Test to output B.
Test t1 = new Test (); // call the Test instance constructor to output B
Test1 t2 = new Test1 (); // call the instance constructor of Test to output B, and then call the instance constructor of Test1 to Output c
Therefore, the final output is: a, B, c
The static constructor is called only once.
Let's look at an example:
In this example, the class Bus has a static constructor and a static member Drive (). When Drive () is called, the static constructor is called to initialize the class.
C #
Copy code
Public class Bus
{
// Static constructor:
Static Bus ()
{
System. Console. WriteLine ("The static constructor invoked .");
}

Public static void Drive ()
{
System. Console. WriteLine ("The Drive method invoked .");
}
}

Class TestBus
{
Static void Main ()
{
Bus. Drive ();
}
}
The output result is as follows:
The static constructor invoked.

The Drive method invoked.
Remember that the static constructor is always called before the instance constructor. (The static constructor can exist in the static class, but the instance constructor cannot exist)
Let's look at an example:
/*************************************** ***********
* Static structure function training
* (1) ① ③ ...... Execution Order
* (2) output result: static ()
* Static B ()
* X = 1, Y = 2
**************************************** ***********/
Class
{
Public static int X;
Static A () // ④ return to ③ After execution
{
X = B .Y + 1;
Console. WriteLine ("static ()");
}
}
Class B
{
Public static int Y = A.X + 1; // ③ call the static member of,
// Go to the static constructor of A ---->
Static B () // ② If the static field with the Initial Value Setting item,
// When executing the static constructor of this class,
// Execute the initial values in the text order first.
// Go to the initial value setting item ---->
{
Console. WriteLine ("static B ()");
}
Static void Main () // ① program entry,
// If the class contains the Main method used to start execution,
// The static constructor of this class will be executed before the Main method is called.
// Go to the static constructor of B ---->
{
Console. WriteLine ("X = {0}, Y = {1}", A.X, B .Y); // ⑤ output result
Console. ReadLine ();
}
}
For more details about the static constructor, refer:
Http://msdn2.microsoft.com/zh-cn/library/k9x6w0hc (VS.80). aspx
Http://www.yaosansi.com/blog/article.asp id = 730

3. The basic knowledge of static classes and static constructors is introduced earlier. Here we will understand some knowledge related to the singleton mode.
1. BeforeFieldInit flag
Relationship between static constructors and type initializers
Static constructor definition:
The C # specification (ECMA 334) states in section 17.11:

The static constructor for a class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
An instance of the class is created.
Any of the static members of the class are referenced.

That is to say, when the class is instantiated or the static member of the class is referenced, the static constructor is called. In the same AppDomain, the static constructor of a class can be called at most once.
Let's take a look at the definition of the type initiator:
The CLI specification (ECMA 335) states in section 8.9.5:
A type may have a type-initializer method, or not.
A type may be specified as having a relaxed semantic for its type-initializer method (for convenience below, we call this relaxed semantic BeforeFieldInit)
If marked BeforeFieldInit then the type's initializer method is executed at, or sometime before, first access to any static field defined for that type
If not marked BeforeFieldInit then that type's initializer method is executed at (I. e., is triggered ):
First access to any static or instance field of that type, or
First invocation of any static, instance or virtual method of that type

That is to say, the system has a mechanism to determine when the type initiator is called (it is determined by a BeforeFieldInit maintained by the system itself)
When BeforeFieldInit is marked, the type initiator will be called when the type of static field is accessed for the first time or before (that is, the time when the type initiator is called is unknown, you can only determine the time when it is called <= the time when you first access the static field of the type)
When BeforeFieldInit is not marked, the type initiator is called (whether static or instance members) when you first access a member of this type ).
Now let's go to the key: the definition of C # states that BeforeFieldInit will be automatically marked only when a type does not have a static constructor. In fact, this is done by the compiler, which may lead to some unexpected results.
Finally, I want to emphasize that the static constructor is not the same as the type initiator. All types have a type initializer, but not necessarily a static constructor.
We already know what the static constructor is, but what is the type initiator?
The Type initiator is actually responsible for initializing static fields for us. Suppose we have defined such a field, static object o = new object (); if the class where the field is located does not have a static constructor, the initialization of o is completed by the Type initiator, and the initialization of o is done by the static constructor.
Is it hard to understand? Let's look at a specific example:
Class Test // No static constructor, BeforeFieldInit is marked
{
Static object o = new object ();
}

Class Test
{
Static object o;

Static Test () // There is a static constructor, BeforeFieldInit is not marked
{
O = new object ();
}
}

Do you think the two classes above are equivalent?

In fact, the above two classes are not equivalent. Because the first class does not have a static constructor, its BeforeFieldInit is marked, and the second class is not marked because its BeforeFieldInit has a static constructor. Therefore, the call time of their type initiators is not the same. For more information about the call time, see the preceding description.
Let's look at a class.
Class Test
{
Static object o = new object ();

Static Test ()
{
}
}
This class is equivalent to the second class above.

Why do we need to check whether BeforeFieldInit is marked? Take the third class as an example. When we add an empty static constructor to Test, its BeforeFieldInit will be set to unmarked. In this way, we can ensure that o will not be initialized until this type of field is accessed for the first time or when the method is called for the first time, so as to implement inert loading.
If we do not add such an empty static constructor, we will not be able to know when o will be initialized (it can only be determined before this type of field is accessed for the first time)

(2) specific implementation

First, let's look at the implementation of the most common Singleton mode, which is also a common method for many people:

Design Patterns: Elements of Reusable Object-Oriented Software [Gamma95]

The solution described in, but modified it to take advantage of the language functions available in C #, such as attributes:

Version 1: Non-thread-safe implementation

// Bad Code! Do not use!

 

Public sealed class Singleton

{

Static Singleton instance = null;

Singleton ()

{

}

 

Public static Singleton Instance

{

Get

{

If (instance = null)

{

Instance = new Singleton ();

}

Return instance;

}}}

This implementation has two main advantages:

Because the Instance is created inside the Instance property method, the class can use additional features (for example, instantiate the subclass), even if it may introduce

Unwanted dependencies.

It is called "lazy instantiation" until the object requires an instance to be generated ". Lazy instantiation avoids unnecessary instantiation during application startup

Singleton.

However, the main disadvantage of this implementation is that it is insecure in a multi-threaded environment.

If the if (instance = null) Statement is determined by different threads during execution and the instance is found to be null, multiple Singleton object instances may be created.

There are many ways to solve this problem.

Version 2: thread security Lite version

Public sealed class Singleton

{

Static Singleton instance = null;

Static readonly object padlock = new object ();

Singleton ()

{

}

Public static Singleton Instance

{

Get

{

Lock (padlock)

{

If (instance = null)

{

Instance = new Singleton ();

}

Return instance;

}}}}

 

(3)

In the second version, we achieved thread security. At the same time, we have also implemented the inertia loading mechanism.

The only drawback of this version is that lock may affect the efficiency of a large number of concurrent accesses. However, in general applications, such efficiency loss is negligible.

If the access efficiency is critical to our lives, we can improve it to the third version.

Version 3: double-check locking ensures thread security

 

// Bad Code! Do not use!

Public sealed class Singleton

{

Static Singleton instance = null;

Static readonly object padlock = new object ();

Singleton ()

{

}

Public static Singleton Instance

{

Get

{

If (instance = null)

{// @ Location 1 this location may have multiple threads in waitzhuangtai

Lock (padlock)

{

// Repeat the test once. Even if @ location 1 already has multiple threads flooding in, the Singleton initialization will not be repeated.

If (instance = null)

{

Instance = new Singleton ();

}}}

Return instance;

}}}

 

You can think about the above Code. It seems that the double check lock mechanism has improved the access efficiency and ensured thread security. However, there are many such expressions

Platform and compiler optimization are incorrect.

The reason is: instance = new Singleton (); the behavior of this line of code on different compilers is unpredictable. An optimized compiler can be implemented as follows:

Instance = new Singleton ();

1. instance = allocate memory to new entities

2. Call the Singleton constructor to initialize the member variable of the instance (the initialization here refers to the instance Member that initializes the instance, because Singleton

The static member has been initialized)

Now imagine that threads A and B are calling the instance, thread A first enters, And the cpu is kicked out when the process reaches step 1. Then thread B enters. What B sees is

Instance is no longer null

(The memory has been allocated), so it began to use the instance with confidence, but this is wrong, because at this time, the instance's member variables are still default values,

A has not had time to execute Step 2 to complete instance initialization.

Of course, the compiler can also achieve this:

1. temp = Memory Allocation

2. Call the temp Constructor

3. instance = temp

If the compiler behavior is like this, we seem to have no problem, but the fact is not that simple, because we cannot know how a compiler works.

To solve this problem, we can use the memory wall mechanism (MemoryBarrier) to solve this problem. The above code is rewritten as follows:

 

Version 3 to version 1

Public sealed class Singleton

{

Static Singleton instance = null;

Static readonly object padlock = new object ();

Singleton ()

{

}

Public static Singleton Instance

{

Get

{

If (instance = null)

{

Lock (padlock)

{

If (instance = null)

{

Singleton newVal = new Singleton ();

System. Threading. Thread. MemoryBarrier ();

Instance = newVal;

}}}

Return instance;

}}}

 

Version 3 changed to Version 2

Public sealed class Singleton

{

Static volatile Singleton instance = null;

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;

}}}

The concepts mentioned above, such as multi-thread security, double risk lock, memory wall, and so on, involve a lot of compilation principles and Assembly knowledge. Due to limited personal capabilities, it is impossible to further elaborate on them, if you are interested, refer to the following urls:

Http://msdn.microsoft.com/msdnmag/issues/05/10/MemoryModels/default.aspx

Http://blogs.msdn.com/brada/archive/2004/05/12/130935.aspx

Http://www.cs.umd.edu /~ API/java/memoryModel/DoubleCheckedLocking.html

Http://blog.joycode.com/demonfox/archive/2007/01/04/90894.aspx

Http://www.cnblogs.com/dayouluo/archive/2005/12/25/304455.html

 

So far, we have considered both efficiency, thread security, and inert loading in the two Release versions of the third version.

However, although there are no problems with the application, these two editions seem to involve too much professional knowledge and complicate the problem,

It makes people feel unfamiliar.

In this case, let's look at a more friendly version:

Version 4: simple implementation and non-inert Loading

Public sealed class Singleton

 

{

Private static readonly Singleton instance = new Singleton ();

Private Singleton (){}

Public static Singleton Instance

{

Get

{

Return instance;

}}}

 

This implementation is simple and thread-safe. The main defect is that the instance is not loaded with inertia. To be accurate, it is not necessarily a lazy load, because we cannot know the instance.

When will it be initialized. If you have any concerns about my description, please refer to the singleton mode for in-depth research (1 ).

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.