C # Lock Interpretation

Source: Internet
Author: User

I recently studied. Net distributed cache code, which involves lock. I read some articles on the Internet and summarized some lock-related knowledge for your reference.

I. Lock Definition

The lock keyword can be used to ensure that the code block is executed without being interrupted by other threads. It can define a piece of code as a critical section. A mutex section allows only one thread to run at a time, while other threads must wait. This is achieved by obtaining mutex locks for a given object during the code block operation.

In multiple threads, each thread has its own resources, but the code zone is shared, that is, each thread can execute the same function. This may cause several threads to execute a function at the same time, resulting in data confusion and unexpected results. Therefore, we must avoid this situation.

While in. net, it is best to understand the process, application domain and thread concepts, Because lock is for the Thread level, and in. is the application domain in net isolated from lock? I guess that threads in different application domains cannot be interrupted through lock; in addition, it is best to understand the concepts of data segments, code segments, stacks, and stacks.

The C # Lock keyword is defined as follows:

Lock (expression) statement_block, where expression represents the object you want to trace, usually the object reference.

If you want to protect an instance of a class, generally you can use this; if you want to protect a static variable (such as a mutex code segment inside a static method ), generally, you can use a class name.

The statement_block is the code of the mutex, which can be executed by only one thread at a time.

Ii. Simple Example

Using system;
Using system. collections;
Using system. Collections. Generic;
Using system. Threading;
Namespace consoleapplication1
{
Class Program
{
Static void main (string [] ARGs)
{
Thread thread1 = new thread (New threadstart (threadstart1 ));
Thread1.name = "thread1 ";
Thread thread2 = new thread (New threadstart (threadstart2 ));
Thread2.name = "thread2 ";
Thread thread3 = new thread (New threadstart (threadstart3 ));
Thread3.name = "thread3 ";
Thread1.start ();
Thread2.start ();
Thread3.start ();
Console. readkey ();
}
Static object _ OBJECT = new object ();
Static void done (INT millisecondstimeout)
{
Console. writeline (string. format ("{0}-> {1 }. start ", datetime. now. tostring ("HH: mm: SS"), thread. currentthread. name ));
// The code segment below can only be executed by one thread at a time
Lock (_ OBJECT)
{
Console. writeline (string. format ("{0}-> {1} to enter the locked area. ", datetime. now. tostring ("HH: mm: SS"), thread. currentthread. name ));
Thread. Sleep (millisecondstimeout );
Console. writeline (string. format ("{0}-> {1} exit the locked area. ", datetime. now. tostring ("HH: mm: SS"), thread. currentthread. name ));
}
}
Static void threadstart1 ()
{
Done (5000 );
}
Static void threadstart2 ()
{
Done (3000 );
}
Static void threadstart2 ()
{
Done (1000 );
}
}
}

Iii. briefly explain the execution process

Let's take a look at the execution process. The sample code is as follows:

Private Static object OJB = new object ();

Lock (OBJ)

{

// Lock the Running code segment

}
Assume that thread a is executed first, and thread B is a little slower. Thread a executes the lock statement to determine whether the OBJ has applied for a mutex lock. It is determined that the object is implemented one by one with an existing lock. referenceequals comparison (not confirmed here). If it does not exist, apply for a new mutex lock. Then thread a enters the lock.

In this case, it is assumed that thread B is started, and thread A has not executed the code in the lock. Thread B executes the lock statement and checks that obj has applied for a mutex lock, so it waits until thread a finishes executing the lock and releases the mutex lock, thread B can apply for a new mutex lock and execute the code in the lock.

Iv. Selection of lock objects

Next, let's talk about the lock object.

1. Why cannot I lock the value type?

For example, what about lock (1? Lock is essentially the monitor. Enter, and monitor. Enter will bind the value type. Each lock is the boxed object. The lock is similar to the syntax sugar of the compiler. Therefore, the compiler directly limits the lock value type. Even if the compiler allows you to lock (1), but the object. referenceequals () always returns false (because it is a different object after each packing), that is, each time it is determined that no mutex lock is applied, so that at the same time, other threads can still access the code in it and cannot achieve the synchronization effect. Similarly, lock (object) 1) does not work.

2. Lock string

What about the lock ("XXX") string? The original words on msdn are:

Locking a string is especially dangerous because the string is "Temporarily" by the Common Language Runtime Library (CLR ". This means that there is only one instance for any given string in the entire program, and the same object represents the text in all threads in all running application domains. Therefore, as long as a lock is placed on a string with the same content anywhere in the application process, all instances of the string in the application will be locked.

3. Lock objects recommended by msdn

Generally, it is better to avoid locking the public type or locking an object instance that is not controlled by the application. For example, if the instance can be publicly accessed, lock (this) may be faulty because uncontrolled Code may also lock the object. This may cause a deadlock, that is, two or more threads are waiting to release the same object. For the same reason, locking public data types (compared to objects) may also cause problems.

Besides, lock (this) is only valid for the current object. If multiple objects cannot be synchronized.

For custom classes, private read-only static objects are recommended, for example:

Private Static readonly object OBJ = new object ();

Why should I set it to read-only? At this time, if the OBJ value is changed in the lock code segment, other threads will be unobstructed. Because the mutex lock object has changed, object. referenceequals will inevitably return false.

4. Lock (typeof (class ))

Like locking strings, the range is too wide.

5. Special questions: detailed explanations of lock (this)

In previous programming, I used lock (this) to solve the lock problem. After a problem occurs, I looked at msdn and suddenly found the following lines: Generally, we should avoid locking the public type, otherwise, the instance is out of the control scope of the Code. Common structures such as lock (this), lock (typeof (mytype), and lock ("mylock") violate this rule: if an instance can be accessed by the public, C # Lock this problem will occur. If mytype can be accessed by the public, the lock (typeof (mytype) problem will occur. Any other code that uses the same string in the process will share the same lock, so the lock ("mylock") problem occurs.

Let's take a look at the issue of C # Lock this: if there is a class class1, this class has a method to implement mutual exclusion using lock (this:

  1. Publicvoidmethod2 ()
  2. {
  3. Lock (this)
  4. {
  5. System. Windows. Forms. MessageBox. Show ("method2end ");
  6. }
  7. }

In the same class1 instance, The method2 can be mutually exclusive. However, if two class1 instances run method2 respectively, there is no mutex effect. Because the lock here only locks the current instance object.

Lock (typeof (mytype) locks a wider range of objects, because all instances of a class have only one type object (this object is the return result of typeof), lock it, it locks all instances of this object. Microsoft recommends that you do not use lock (typeof (mytype) because locking a type object is a very slow process, other threads in the class, or even other programs running in the same application domain, can access this type of object. Therefore, they may be used to lock the type object instead of you, it completely blocks your execution and causes your own code to be suspended.

Locking a string is even more amazing. As long as the string content is the same, it can cause the program to be suspended. The reason is that in. net, the string will be temporarily stored. If the two variables have the same string content,. NET will allocate the temporary String object to the variable. So if both of them are using lock ("My lock"), they actually lock the same object. At this point, Microsoft provides a recommended lock usage: locking a private static member variable.

. Net in some collection classes (such as arraylist, hashtable, queue, stack) has provided a Lock Object syncroot, and used the reflector tool to view the syncroot attribute code. In the array, this attribute has only one sentence: return this, which is the same as the current instance of lock array. Syncroot in arraylist is different.

  1. Get
  2. {
  3. If (this. _ syncroot = NULL)
  4. {
  5. Interlocked. compareexchange (refthis. _ syncroot, newobject (), null );
  6. }
  7. Returnthis. _ syncroot;

The interlocked class provides atomic operations for variables shared by multiple threads (use this class if the object you want to lock is of the basic data type ), the compareexchange method compares the current syncroot with null. If they are equal, replace them with the new object () method to ensure thread security when multiple threads use syncroot. Another method in the Collection class is related to synchronization: Synchronized. This method returns a Wrapper class of the corresponding collection class, which is thread-safe, because most of his methods use lock for synchronization, such as the add method:

  1. Publicoverrisponidadd (objectkey, objectvalue)
  2. {
  3. Lock (this. _ table. syncroot)
  4. {
  5. This. _ table. Add (Key, value );
  6. }
  7. }

It is important to note that enumeration of a set from start to end is not a thread-safe process. Even if a set has been synchronized, other threads can modify the set, which causes an exception in the number of enumerations. To ensure thread security during enumeration, you can lock the set throughout the enumeration process:

  1. Queuemycollection = newqueue ();
  2. Lock (mycollection. syncroot ){
  3. Foreach (objectiteminmycollection ){
  4. // Insertyourcodehere.
  5. }
  6. }

Last

Note: Do not lock the public type; otherwise, the instance will be out of the control scope of the Code. Common structures such as lock (this), lock (typeof (mytype), and lock ("mylock") violate this rule:
1) if the instance can be accessed by the public, the lock (this) problem will occur;
2) If mytype can be accessed by the public, the lock (typeof (mytype) problem will occur;
3) any other code using the same string in the process will share the same lock, so the lock ("mylock") problem occurs;
The best practice is to define private objects to lock, or private static object variables to protect the data shared by all instances.

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.