Visual Basic. Net implements dual-check lock (DCL) mode-http://www.wewill.cn/n231c7.aspx

Source: Internet
Author: User
News introduction:This article introduces the code mode called double-check locking (DCL) mode. It works in singleton mode and multiton mode) and discusses the DCl mode in Visual Basic. net and C. The source code of Visual Basic. Net can be seen in the text. The source code of C # is provided in the appendix.
This document assumes that the reader is familiar with the multi-threading concepts of Visual Basic. Net or C #, the basic concepts of design patterns, and the basic UML icons.
The DCl pattern (double-check locking pattern) is also known as the double-check pattern. It is only useful in multi-threaded environments. It

Key words:. Net Visual Basic Mode
This article introduces the code mode called double-check locking (DCL) mode. It works in singleton mode and multiton mode) and discusses the DCl mode in Visual Basic. net and C. The source code of Visual Basic. Net can be seen in the text. The source code of C # is provided in the appendix.

This document assumes that the reader is familiar with the multi-threading concepts of Visual Basic. Net or C #, the basic concepts of design patterns, and the basic UML icons.

The DCl pattern (double-check locking pattern) is also known as the double-check pattern. It is only useful in multi-threaded environments. It is transplanted from the C language. In C language, the DCl mode is often used in late instantiation of classes in multi-threaded environments.

The DCl mode is usually used together with the factory mode to reuse product objects cyclically. If the reader is familiar with the singleton mode, the DCl mode can be used in the "lazy" Singleton mode to provide unique product objects. Through further promotion, we can use the multiton and flyweight modes.

  Starting from the factory Mode

To explain what the DCl mode is, let's start with the factory mode.

In the following class diagram, the factory class factory0 has a sharing method getinstance () used to provide the product class product instance.


Figure 1. A system composed of the factory class and product class.

The source code of factory0 is as follows:

Public class factory0
Public shared function getinstance () as product
Return New Product ()
End Function
End Class
Code List 1. source code of the factory0 class

Obviously, you only need to call the getinstance () method to obtain the product class instance, and each call gets a new instance. The product class provides a counting method. You can call the getcount () method to obtain the total number of all instances of the product.

Public class product
Private shared count as integer = 0

Public sub new ()
Count + = 1
System. Console. writeline ("product number {0} is created.", count)
End sub

Public shared function getcount () as integer
Return count
End Function
End Class

Code List 2. Source Code of product products

However, if the product instance must be used cyclically, but cannot be created without limit, the content of the factory method getinstance () must be rewritten to implement the necessary cyclic logic. The simplest cyclic logic is to reuse a single product instance. For example, the following source code implements the logic of a single product instance:

Public class factory1
Private shared instance as product

Public shared function getinstance () as product
If (instance is nothing) then
Instance = new product ()
End if
Return instance
End Function
End Class

Code List 3. Source Code of factory factory1

Isn't it easy anymore? If you have already created a product-type instance, the instance will be returned. Otherwise, you will first create the instance, record it, and then return it.

To write such code, it is obvious that there is only one product instance in the system; therefore, if (instance is nothing) then check will be performed. Obviously, if the code is run in a multi-threaded environment, two or more product objects will be created in the above Code, resulting in errors.

In a multi-threaded environment, if two threads a and B almost reach the IF (instance is nothing) Then Statement at the same time, assuming that thread a is a little earlier than thread B, then:

1. A will first enter the IF (instance is nothing) then block and start executing the new product () statement. At this time, the instance variable is still nothing until the new product () Statement of thread a returns and assigns a value to the instance variable.

2. however, thread B does not wait outside the IF (instance is nothing) then statement, because at this time the instance is nothing and it will immediately enter if (instance is nothing) inside the then block. In this way, thread B will inevitably execute the statement of instance = new product () to create a second instance.

3. After the execution of the Instance = new product () Statement of thread a is completed, the instance variable gets a real object reference and the instance is no longer true. The third thread will not be in the IF (instance is nothing) then statement block.

4. Then, the instance = new product () Statement of thread B is executed, and the value of the instance variable is overwritten. However, the fact that the first product object is referenced by thread A does not change.

At this time, thread a and thread B each have an independent product object, which is wrong. To intuitively view the execution result of the program, run the following client code:

Private sub run1 ()
Dim o as product
O = factory1.getinstance
System. Console. writeline ("Total number of objects created: {0}", O. getcount)
End sub

Private sub btncreatemediaclick (...) Handles btncreate1.click
Dim T (9) as thread
Dim count as integer

For Count = 0 to 9
T (count) = new thread (addressof run1)
T (count). Start ()
Next
End sub

Code list 4. source code of the Client

In addition, add the following to the first line of the getinstance () method of factory1:

Thread. Sleep (10)

Statement, equivalent to simulating a lengthy product creation process, so that the first thread to enter is waiting for the following thread, thus highlighting the problem of multithreading.

The above client code uses 10 threads to call the factory method at the same time, and then calls the product counting method to print out the total number of instances of the product class. If you run the code, you will find that the factory method will create more than one product instance. When I run this code, the system generates nine product instances.

Therefore, factory1 fails in a multi-threaded environment as a factory that uses the product instance cyclically. If you use a client similar to the Code in Listing 4, you can see that the system has created only one product instance from start to end.

  A thread-safe version

To overcome the disadvantages of no thread security, the following provides a thread-safe getinstance () method:

<Methodimpl (methodimploptions. Synchronized)> _
Public shared function getinstance () as product
Thread. Sleep (10)

If (instance is nothing) then
Instance = new product ()
End if

Return instance
End Function

Code List 5. This is the correct answer to thread security.

Obviously, because the entire static factory method is synchronized, there will be no two threads entering this method at the same time. Therefore, when thread a and thread B call this method simultaneously or almost simultaneously as the first batch of callers:

When thread a arrives a little earlier, it will first enter this method, and thread B will wait outside the method;

1. For thread A, the value of the instance variable is nothing, so the instance = new product () statement will be executed.

2. Thread a stops executing the method. The value of the instance variable is no longer nothing.

3. Thread B enters this method. The value of the instance variable is no longer nothing, so the instance = new product () statement will not be executed. Thread B obtains the reference contained in the instance variable, that is, the reference to the product instance created by thread.

Obviously, it is correct that thread a and thread B hold the same product instance.

After reading this article, you can refer to questions 1, 2, and 3.

  Optimized thread security version-DCL Mode

Before proceeding to the discussion in this Section, first review the mutex class. Mutex can provide exclusive access restrictions to achieve synchronization by allowing only one thread to access this resource. To obtain access permission, you must call the waitone () method. If no other thread is currently accessed, the thread can obtain access permission; otherwise, the thread will wait at this statement. When the access ends, you can call the releasemutex () method to release the access permission.

After carefully reviewing the code listing 5 above, we will find that synchronization is only useful before the instance variable is assigned a value for the first time. After the instance variable has a value, synchronization actually becomes an unnecessary bottleneck. If there is a way to remove this small extra overhead, isn't it more perfect? Therefore, we have the following cleverly designed double-check locking ).

Public class factory3
Private shared instance as product
Private shared M as mutex = new mutex ()

Private sub new ()
System. Console. writeline ("factory object is created .")
End sub

Public shared function getinstance () as product
Thread. Sleep (10)
If (instance is nothing) then'' location 1
''Location 2
M. waitone ()
''Location 3
If (instance is nothing) then'' location 4
Instance = new product ()
End if
M. releasemutex ()
End if

Return instance
End Function
End Class

Code List 6: lazy factory classes using DCL

For the readers who first came into contact with the DCl model, the idea of this technique is not obviously easy to understand. Therefore, this article provides a detailed explanation here. Similarly, we assume that thread a and thread B, as the first batch of callers, call static factory methods at the same time or almost simultaneously.

1. Because threads A and B are the first callers, the instance variable is nothing when they enter this static factory method. Therefore, thread a and thread B arrive at location 1 at the same time or almost simultaneously.

2. Assume that thread a will first reach Location 2, enter M. waitone (), and reach location 3. At this time, due to the synchronization restrictions of M. waitone (), thread B cannot reach the position 3, but can only wait at the position 2.

3. Thread a executes the instance = new product () statement to obtain a value for the instance variable, that is, a reference to a product object. At this time, thread B can only wait at location 2.

4. Thread a exits M. waitone (), returns the instance object, and exits the static factory method.

5. Thread B enters the M. waitone () block, reaches position 3, and then reaches position 4. Because the instance variable is no longer a nothing, thread B exits M. waitone (): return the product object referenced by the instance (that is, the product object created by thread a) and exit the static factory method.

So far, thread a and thread B have the same product object. As you can see, in the above method getinstance (), synchronization is only used to prevent multiple threads from initializing this class at the same time, rather than calling this static factory method at the same time. If this is correct, then the "lazy" factory class can get rid of the synchronization bottleneck and reach a perfect realm. This is the DCl mode.

Here, you can see if you can answer questions 4, 5, and 6 after this article.

  DCL Mode

Readers who first came into contact with this technique will certainly have many problems, such as the first or second check, which may be saved. The answer is: according to the multi-thread principle and the DCl model, they cannot be saved.

First, if the first check is omitted, the factory method will become as follows:

Public shared function getinstance () as product
Thread. Sleep (10)
''Location 1
''Location 2
M. waitone ()
''Location 3
If (instance is nothing) then'' location 4
Instance = new product ()
End if
M. releasemutex ()

Return instance
End Function

Code List 7. The factory method for thread security of the first check is omitted

This causes the product instance to wait at location 2, whether or not it exists, that is, the factory method that is equal to the thread security before optimization (see Code List 5 ), although there is no error in generating more than one product object, it does not achieve the goal of optimization.

Second, if the second check is omitted, the factory method mode will become as follows:

If (instance is nothing) then'' location 1
''Location 2
M. waitone ()
''Location 3
Instance = new product ()
M. releasemutex ()
End if
Return instance
Code List 8: The Factory method for thread security that skips the second check

Can this be done? It is also assumed that thread a and thread B, as the first batch of callers, call static factory methods at the same time or almost simultaneously.

1. Because threads A and B are the first callers, the instance variable is nothing when they enter this static factory method. Therefore, thread a and thread B arrive at location 1 at the same time or almost simultaneously.

2. Assume that thread a will first reach Location 2 and enter M. waitone () to reach location 3. At this time, due to the synchronization restrictions of M. waitone (), thread B cannot reach the position 3, but can only wait at the position 2.

3. Thread a executes the instance = new product () statement to obtain a value for the instance variable, that is, a reference to a product object. At this time, thread B can only wait at location 2.

4. Thread a exits M. waitone (), returns the instance object, and exits the static factory method.

5. thread B enters M. waitone () block, reaching position 3. Thread B executes the instance = new product () statement to get a new value for the instance variable and B exits the static factory method.

Therefore, threads a and B create two product class instances. In other words, it is not possible to have a second check.

  Application of DCL mode in singleton Mode

The Singleton mode describes only one instance class, which is called the singleton class. The Singleton class provides its own unique instance to the outside world. Generally, Singleton mode is mostly used in multi-threaded environments, which makes thread synchronization very important.

According to the different creation methods of Singleton instances, the implementation of Singleton mode can be divided into two types: "Hungry" and "lazy ". "Lazy" Singleton mode determines whether to create a product instance when the factory method is called: If the instance already exists, return the instance directly. Otherwise, create an instance first, store the instance and return it to the instance.

Readers familiar with the singleton mode should be aware that the DCl mode can be used in the singleton mode of "lazy. In fact, the singleton class is a special case of the DCl mode. You only need to merge the factory class and product class to get the singleton class. See the following UML class diagram.


Figure 2. A singleton class

The source code of this Singleton class is as follows:

Public class Singleton
Private shared instance as Singleton
Private shared M as mutex = new mutex ()

Public sub new ()
System. Console. writeline ("singleton object is created .")
End sub

Public shared function getinstance () as Singleton
Thread. Sleep (10)
If instance is nothing then
M. waitone ()
If instance is nothing then
Instance = new Singleton ()
End if
M. releasemutex ()
End if
Return instance
End Function
End Class

Code List 9. Singleton class with double check thread security

  DCL model promotion

The implementation of the DCl mode described above is based on the simplest logic, that is, the single instance logic. This logic can be further extended into a more general circular logic.

For example, a factory object can control the maximum number of product instances. If the maximum value is 1, it becomes the logic of a single instance. If the value is greater than 1, it becomes the logic of multiple instances.

If the product object is stateful, although the factory object does not control the number of product instances, it cyclically uses product instances according to the product object status, for example, only one (or N) Product instances in each status are allowed.

  Q &

1st. Use mutex to rewrite Code List 5.
2nd. Use monitor to rewrite Code List 5.
3rd. Use synclock to rewrite Code List 5.
4th. Use monitor to rewrite code list 6.
5th. Use synclock to rewrite code list 6.
6th. Use monitor to rewrite Code List 9.
7th. Use synclock to rewrite Code List 9.

  Q & A answers

1st. The result of Listing 5 is as follows:

Public class factory2a
Private shared instance as product
Private shared M as mutex = new mutex ()

Private sub new ()
System. Console. writeline ("factory object is created .")
End sub

Public shared function getinstance () as product
Thread. Sleep (10)
M. waitone ()

If (instance is nothing) then
Instance = new product ()
End if

M. releasemutex ()
Return instance
End Function
End Class

Code List 10. Singleton class for thread-safe double check

2nd answer: the monitor object provides a synchronization lock for a resource object. Use the monitor object to rewrite Code List 5. The result is:

Public class factory2b
Private shared instance as product

Private sub new ()
System. Console. writeline ("factory object is created .")
End sub

Public shared function getinstance () as product
Thread. Sleep (10)
Monitor. Enter (GetType (factory2b ))

If (instance is nothing) then
Instance = new product ()
End if

Monitor. Exit (GetType (factory2b ))
Return instance
End Function
End Class

Code List 11. Singleton class for thread-safe double check

3rd answer: The synclock version is as follows:

Public class factory2c
Private shared instance as product

Private sub new ()
System. Console. writeline ("factory object is created .")
End sub

Public shared function getinstance () as product
Thread. Sleep (10)
Synclock (GetType (factory2c ))
If (instance is nothing) then
Instance = new product ()
End if
End synclock

Return instance
End Function
End Class

Code List 12. Singleton class for thread-safe double check

4th answer: the factory class of the double check lock after the monitor object is rewritten is:

Public class factory3a
Private shared instance as product

Public shared function getinstance () as product
Thread. Sleep (10)

If (instance is nothing) then
Monitor. Enter (GetType (factory3a ))
If (instance is nothing) then
Instance = new product ()
End if
Monitor. Exit (GetType (factory3a ))
End if
Return instance
End Function
End Class

Code List 13. Singleton class for thread-safe double check

5th answer: the dual-check lock factory class after synclock is rewritten is;

Public class factory3b
Private shared instance as product

Public shared function getinstance () as product
Thread. Sleep (10)

If (instance is nothing) then
Synclock (GetType (factory3b ))
If (instance is nothing) then
Instance = new product ()
End if
End synclock
End if
Return instance
End Function
End Class

Code list 14. Singleton class for thread-safe double check

6th the source code for rewriting the singleton mode with the monitor object is as follows:

Public class singletona
Private shared instance as singletona

Public sub new ()
System. Console. writeline ("singleton object is created .")
End sub

Public shared function getinstance () as singletona
Thread. Sleep (10)
If instance is nothing then
Monitor. Enter (GetType (singletona ))
If instance is nothing then
Instance = new singletona ()
End if
Monitor. Exit (GetType (singletona ))
End if
Return instance
End Function
End Class

Code List 15. Singleton class for thread-safe double check

7th the source code of the singleton mode after synclock is used is as follows:

Public class singletonb
Private shared instance as singletonb

Public sub new ()
System. Console. writeline ("singleton object is created .")
End sub

Public shared function getinstance () as singletonb
Thread. Sleep (10)
If instance is nothing then
Synclock (GetType (singletonb ))
If instance is nothing then
Instance = new singletonb ()
End if
End synclock
End if
Return instance
End Function
End Class

Code List 16. Singleton class for thread-safe double check

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.