Blocking Monitor.Enter and Monitor.Exit Blocks

The ECMA-335 specification states the following:

* Acquiring a lock (System.Threading.Monitor.Enter or entering a synchronized method) implicitly performs an unstable read operation, and releasing a lock (System.Threading.Monitor.Exit or exiting a synchronized method) implicitly performs a volatile write operation. (...)

Volatile reading acquires semantics, meaning that reading is guaranteed to occur before any memory references that occur after a read command in a sequence of CIL commands. Flying recordings have release semantics, meaning that recordings are guaranteed after every write to the write command in the CIL command sequence. *

This means that compilers cannot move statements from Monitor.Enter / Monitor.Exit blocks, but other instructions are not forbidden to move to a block. Perhaps even another Monitor.Enter could be moved to a block (since a volatile record followed by a volatile read can be replaced). So, can the following code:

class SomeClass { object _locker1 = new object(); object _locker2 = new object(); public void A() { Monitor.Enter(_locker1); //Do something Monitor.Exit(_locker1); Monitor.Enter(_locker2); //Do something Monitor.Exit(_locker2); } public void B() { Monitor.Enter(_locker2); //Do something Monitor.Exit(_locker2); Monitor.Enter(_locker1); //Do something Monitor.Exit(_locker1); } } 

turn into the equivalent of the following order:

 class SomeClass { object _locker1 = new object(); object _locker2 = new object(); public void A() { Monitor.Enter(_locker1); //Do something Monitor.Enter(_locker2); Monitor.Exit(_locker1); //Do something Monitor.Exit(_locker2); } public void B() { Monitor.Enter(_locker2); //Do something Monitor.Enter(_locker1); Monitor.Exit(_locker2); //Do something Monitor.Exit(_locker1); } } 

what can lead to deadlocks? Or am I missing something?

+6
source share
2 answers

The ECMA-335 specification is much weaker than what the CLR uses (and every other implementation) .

I remember reading (heresay) about Microsoft's first attempt to port to IA-64 using a weaker memory model. They had so much code of their own, depending on the double-locking idiom (which was broken down by a weaker memory model) that they had just implemented a stronger model on this platform.

Joe Duffy is a great post summarizing the (actual) CLR memory model for us mere mortals. There is also a link to an MSDN article that explains in more detail how the CLR differs from the ECMA-335.

I do not consider this a problem in practice; just assume the CLR memory model, as everyone else. At the moment, no one will create a weak implementation, since most of the code just breaks.

+2
source

When you use lock or Monitor.Enter and Monitor.Exit , these are full fences , this means that it will create a "barrier" in the Thread.MemoryBarrier() when starting or locking " Monitor.Enter " and until the end of blocking " Monitor.Exit " Thus, no operation will move before and after the lock , but note that the operations inside the lock itself can be replaced with other perspectives of the flows, but this was never a problem, since the lock guarantees mutual exclusive, therefore only one thread will execute the code inside the castle at the same time. In any case, reordering, however, will not occur in any thread, that is, with multithreading, entering the same area of ​​the code, they may not see the instructions in the same order.

I highly recommend that you learn more about the MemoryBarrier and the full and half fences in this article.

Edit: Please note that here I describe that lock is a complete fence, but not to mention the β€œdeadlock” that you know, the script you describe will never happen because, as @Hans mentions, reordering will never occur for method calls, that is:

 Method1(); Method2(); Method3(); 

It will be executed sequentially, but the instructions inside them can be changed, for example, when multithreading executes the code inside Method1() ..

+1
source

Source: https://habr.com/ru/post/894050/


All Articles