. NET synchronization and Asynchronization locks (Lock, Monitor) (7), lockmonitor

Source: Internet
Author: User
Tags mscorlib

. NET synchronization and Asynchronization locks (Lock, Monitor) (7), lockmonitor

This article continued: background knowledge about. NET synchronization and Asynchronization (6)


As mentioned in the previous article, locking is a typical way to solve the competition conditions. This article focuses on the most common lock keywords and Monitor provided by. NET.

I. lock keyword Demo
Public object thisLock = new object (); private long index; public void AddIndex () {lock (this. thisLock) {this. index ++; if (this. index> long. maxValue/2) {this. index = 0 ;}
// A large number of operations unrelated to the index} public long GetIndex () {return this. index ;}


In this demo, the code is concise and the logic is simple. One AddIndex method ensures that the field index is between 0 and 100, and the other GetIndex method is used to obtain the value of the field index.

However, this group of demos has many problems or even errors. I will describe them one by one as follows:

1. Forget to synchronize-that is, read/write operations must be locked

The GetIndex method is not locked. Therefore, the value of the field index can be accessed at any time through this method, that is, the value of 101 will be obtained at a certain time point, this is contrary to the original intention.


2. read/write splitting

If the read/write splitting problem occurs, this demo may not be very intuitive, but the Long type does have read/write splitting. For example:

/// <Summary> /// test atomicity /// </summary> public void TestAtomicity () {long test = 0; long breakFlag = 0; int index = 0; task. run () => {base. printInfo ("start to write data cyclically"); while (true) {test = (index % 2 = 0 )? 0x0: 0x1234567890abcdef; index ++; if (Interlocked. read (ref breakFlag)> 0) {break;} base. printInfo ("Exit loop write data") ;}); Task. run () => {base. printInfo ("START cyclic read data"); while (true) {long temp = test; if (temp! = 0 & temp! = 0x1234567890abcdef) {Interlocked. increment (ref breakFlag); base. printInfo ($ "read/write tear: {Convert. toString (temp, 16)} "); break;} base. printInfo ("Exit cyclic read data ");});}Test atomic operations

The 64-bit data structure requires two commands to perform read and write operations. That is to say, if a read operation happens between the two write commands, the data may not be read. Therefore, be cautious with read/write splitting.


3. granularity Error

In the AddIndex method, a large number of operations irrelevant to the index are not necessary in the lock. Although not necessary, it is not wrong. It can only be said that the granularity of the lock is too large, it causes unnecessary performance impact on concurrency.

The following is an example of an incorrect lock granularity:

Public class BankAccount {private long id; private decimal m_balance = 0.0 M; private object m_balanceLock = new object (); public void Deposit (decimal delta) {lock (m_balanceLock) {m_balance + = delta ;}} public void Withdraw (decimal delta) {lock (m_balanceLock) {if (m_balance <delta) throw new Exception ("Insufficient funds "); m_balance-= delta;} public static void ErrorTransfer (BankAccount a, BankAccount B, decimal delta) {. withdraw (delta); B. deposit (delta);} public static void Transfer (BankAccount a, BankAccount B, decimal delta) {lock (. m_balanceLock) {lock (B. m_balanceLock) {. withdraw (delta); B. deposit (delta) ;}} public static void RightTransfer (BankAccount a, BankAccount B, decimal delta) {if (. id <B. id) {Monitor. enter (. m_balanceLock); // A first Monitor. enter (B. m_balanceLock );//... and then B} else {Monitor. enter (B. m_balanceLock); // B first Monitor. enter (. m_balanceLock );//... and then A} try {. withdraw (delta); B. deposit (delta);} finally {Monitor. exit (. m_balanceLock); Monitor. exit (B. m_balanceLock );}}}Wrong lock Granularity

In the ErrorTransfer method, the transfer amount is in the non-active state at the time point between the two transfer methods, and the lock granularity is too small.

In the Transfer method, although the granularity is correct, it is easy to deadlock. The more appropriate method is: RightTransfer.


4. unreasonable lock Methods

Locking a non-private object is a dangerous action, because the non-private type is exposed to the outside world, and the outside world can also lock the exposed object, in this case, the granularity of locks may be deadlocked or incorrect.

The more reasonable way is to change thislock to private.

And so on:

1. lock (this): a similar problem occurs if the current type is externally accessible.

2. lock (typeof (T): The Type object is unique in the entire process domain. Therefore, if T is an externally accessible type, a similar problem may occur.

3. lock ("String"): because of the special nature of the String type (memory resident mechanism), multiple strings may actually be the same lock, so, when you are not careful, it is easy to fall into the trap, resulting in deadlocks or wrong lock granularity.


Ii. view the essence through IL code

The following is all the il code of the AddIndex method [using the. NET 4.5 class library, VS2015 compilation]:

. Method public hidebysig instance void AddIndex () cel managed {// code size 81 (0x51 ). maxstack 3. locals init ([0] object V_0, [1] bool V_1, [2] bool V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: ld1_object ParallelDemo. demo. lockMonitorClass: thisLock IL_0007: stloc.0 IL_0008: ldc. i4.0 IL_0009: stloc.1. try {IL_000a: ldloc.0 IL_000b: ldloca. s V_1 IL_000d: call void [mscorlib] System. threading. monitor: Enter (object, bool &) IL_0012: nop IL_0013: nop IL_0014: ldarg.0 IL_0015: ldarg.0 IL_0016: ld1_int64 ParallelDemo. demo. lockMonitorClass: index IL_001b: ldc. i4.1 IL_001c: conv. i8 IL_001d: add IL_001e: st1_int64 ParallelDemo. demo. lockMonitorClass: index IL_0023: ldarg.0 IL_0024: ld1_int64 ParallelDemo. demo. lockMonitorClass: index IL_0029: ldc. i8 0x3fffffffffffff IL_0032: cgt IL_0034: stloc.2 IL_0035: ldloc.2 IL_0036: brfalse. s IL_0042 IL_0038: nop IL_0039: ldarg.0 IL_003a: ldc. i4.0 IL_003b: conv. i8 IL_003c: stfld int64 ParallelDemo. demo. lockMonitorClass: index IL_0041: nop IL_0042: nop IL_0043: leave. s IL_0050} // end. try finally {IL_0045: ldloc.1 IL_0046: brfalse. s IL_004f IL_0048: ldloc.0 IL_0049: call void [mscorlib] System. threading. monitor: Exit (object) IL_004e: nop IL_004f: endfinally} // end handler IL_0050: ret}/end of method LockMonitorClass: AddIndexIL

Of course, you do not need to fully understand it. You only need to pay attention to three details:

1. Call [mscorlib] System. Threading. Monitor: Enter (object,Bool &)Method. The second input parameter is the local variable with the index of 1. [Check the class library and find that this parameter is a ref transfer reference].

2. If the local variable with index 1 is not false, call the [mscorlib] System. Threading. Monitor: Exit (object) method.

3. try... finally statement Block

In other words, the lock keyword is essentiallyThe simplified implementation method of the Monitor class, for security, try... finally processing.


III, The wait andPulse

Because the entry lock and Exit lock both have certain performance loss, when there are frequent and unnecessary lock operations, the performance will be more affected.

For example, in the producer consumer mode, frequent lock operations are unnecessary if there is no data to be consumed (polling mode, not push ).

In this case, the wait method comes in handy. The following is a remark in MSDN:

The thread that owns the lock on the specified object calls this method to release the object so that another thread can access it. The caller is blocked when the lock is retrieved again. This method is called when the caller needs to wait for another thread to change its status.


The wait and pulse methods are taken over each other, and there are not many methods used by the author.



Coming soon, next article Introduction: Lock (ReaderWriterLockSlim) (expected 1 Article)

Appendix, Demo: http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip

See more: Casual guide: synchronous and asynchronous

(To be continued ...)


Related Article

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.