Understanding Monitor.Enter(Object)

  • Thread starter Thread starter Clark Sann
  • Start date Start date
C

Clark Sann

Can someone help me understand what object should be used as the lock
object? I've seen some programs that use Monitor.Enter(Me). Then, in those
same programs, they sometimes use another object. Why? I would appreciate
it if someone could help me understand the ramifications of picking an
object in this command.

Thank you so much for your help.

Clark
 
Can someone help me understand what object should be used as the lock
object? I've seen some programs that use Monitor.Enter(Me).

The object should be what needs protection from contention in a multi-thread
environment. If you don't use multiple threads, you done need
Monitor.Enter/Exit (or the equivalent SyncLock). Consider the case of adding
an element to collection colData. If you don't launch threads, no worries.
But if you do launch threads and two independant threads might want to add an
element to the collection at the same time, then you should do this
Threading.Monitor.Enter(colData)
colData.Add(...)
Threading.Monitor.Exit(colData)
or the equivalent SyncLock.
 
Thanks for your reply

I'm still not sure I understand. Here's what I believe about locks and
Monitor... please correct me if I'm wrong.

Monitor - used to lock critical sections of code so only one thread can
execute that section of code at a time

What is locked? - the section of code between Monitor.Enter(object) and
Monitor.Exit(object)

All locks have a key. What is the key? - The key to the lock is the object
passed to the Enter and Exit methods.

How is the key modified to reflect that it has been used to lock a section
of critical code? - I have no idea. This really stumps me. The key can be a
Byte, I guess. So what the heck does the Net framework do to a Byte so that
some other thread knows it has already used it to lock a particular section
of code?????

What about the visibility of the key? Well I guess the key object must be
visible to all threads that will access the critical section of code. Have I
missed anything here?

What happens if several different sections of code are locked with the same
key? I suppose things will get pretty screwed up. For example, if ThreadA
uses ObjectA to lock a section of code. Then, while it is executing that
section of code, ThreadB trys to use the same key to lock another section of
code that is curently not being executed by any threads. I suspect, that
ThreadB will examine the key and find it has already been used so it will
block waiting for the section of code to unlock, even though it is not
actually being executed by a thread at this time. We have lost
synchronization because the key was used improperly.

If I am going to lock a section of code, does it matter what object I use?
Can I use a public Int, a public String, Me, or whatever as the lock? I
guess so. As long as you are sure the key is not used to lock multiple
sections of code that could be executed at the same time and as long as you
are sure the key has the required visibility.

I have a worry that my conclusions are wrong here....that I fundamentally
misunderstand something. Comments?
 
Monitor - used to lock critical sections of code so only one thread can
execute that section of code at a time

Not quite. It locks 'that section' and any other section that is also
locked with the same object.
What is locked? - the section of code between Monitor.Enter(object) and
Monitor.Exit(object)

Same as above - all sections of code locked with the same object.
All locks have a key. What is the key? - The key to the lock is the object
passed to the Enter and Exit methods.

a reasonable metaphor.
How is the key modified to reflect that it has been used to lock a section
of critical code? - I have no idea. This really stumps me.

I don't know what the internal implementation is, but I'm sure it does not
modify the key in any way at all.
The key can be a
Byte, I guess. So what the heck does the Net framework do to a Byte so that
some other thread knows it has already used it to lock a particular section
of code?????

Don't use value types (bytes, integers) with Enter/Exit - use objects. If
you use a value type, .net will box it into an object anyway. I believe that
it is safe to assume that .net makes a copy of the object reference at Enter
and destroys it at Exit. But it does not mess with value types.
What about the visibility of the key? Well I guess the key object must be
visible to all threads that will access the critical section of code. Have I
missed anything here?

Yes. If the object is not visible to a section of code, then that section
will not be able to Enter/Exit on that object.
What happens if several different sections of code are locked with the same
key?

Do you mean at coding time or at runtime? It is normal to code this way, ie
several sections of code with Enter/Exit referencing the same object. But at
runtime, several sections can't be simultaneously locked with the same key.
When one section of code is between Enter and Exit, no other section of code
will be between Enter and Exit on the same object. This is an
inconsequential distinction in a single thread environment.
I suppose things will get pretty screwed up. For example, if ThreadA
uses ObjectA to lock a section of code. Then, while it is executing that
section of code, ThreadB trys to use the same key to lock another section of
code that is curently not being executed by any threads.
I suspect, that
ThreadB will examine the key and find it has already been used so it will
block waiting for the section of code to unlock, even though it is not
actually being executed by a thread at this time.

Correct. ThreadB blocks until ThreadA does Monitor.Exit. Don't think of
one section of code being protected - possibly several different sections of
code could be so protected by locking on the same object. Imagine an object
with a two properties (x and y), and imagine two update sequences that need
to be done in a multithread environment. Update1 does x=1 & y=2, and update2
does x=3 & y=4. Absent some form of locking, a multithreaded near
simultaneous invocation of update1 and update2 could result in x=1 & y=4 or
x=3 & y=2. If you don't see this, imagine thread1 doing half of update1,
then thread2 doing update2, and then thread1 finishing update1. Enter/Exit
(and SyncLock) are designed to solve this problem and all its more complex
variants. All you need to do is code Enter/Exit (or Synclock) appropriately
in update1 and update2, and this functionality has been made thread safe.

If the object is MyObj, and if your updates are coded like MyObj.x=1 (etc),
then you would do Enter(MyObj) and Exit(MyObj). If you were to code update1
and update2 as methods in MyObj's class, then you would use Enter(Me) and
Exit(Me) in these methods. In this way, you can have multiple instances of
the class operating in a multi-threaded environment without these updates
stepping on eachother.
We have lost
synchronization because the key was used improperly.

No, see above.
If I am going to lock a section of code, does it matter what object I use?

No, in this case, it wouldn't. But don't think in terms of locking a
section of code. Think in terms of many sections of code all being locked
with the same object.
Can I use a public Int, a public String, Me, or whatever as the lock? I
guess so. As long as you are sure the key is not used to lock multiple
sections of code that could be executed at the same time and as long as you
are sure the key has the required visibility.

Don't lock with value types. Use objects.
I have a worry that my conclusions are wrong here....that I fundamentally
misunderstand something. Comments?

I know it is complicated.
 
Thank you again for your reply. Your thoughts have been very helpful to me.
I have a few additonal comments and questions....

AMercer said:
Not quite. It locks 'that section' and any other section that is also
locked with the same object.

So, continuing with my analogy, when the key is used, ALL locks that used
the key are locked at the same time. Sort of a wireless key or something
that can lock several locks at once....
Do you mean at coding time or at runtime? It is normal to code this way, ie
several sections of code with Enter/Exit referencing the same object. But at
runtime, several sections can't be simultaneously locked with the same key.
When one section of code is between Enter and Exit, no other section of code
will be between Enter and Exit on the same object.

I'm not totally sure what you mean by "on the same object". I think you
mean that several sections of code are locked by the same locking object.

Lets see if I get this or not. Suppose ThreadA is executing some code and
it enters a critical section locked by MyLockingObject. When it gets to
Monitor.Enter(MyLockingObject), MyLockingObject is tweaked in some way we
don't need to understand, and the thread begins executing the critical
section. Now suppose a thread switch occurs and ThreadB is started. It
encounters a Monitor.Enter(MyLockingObject) in a different section of code.
Because that code is locked using MyLockingObject, ThreadB will be blocked
until execution is switched back to ThreadA and ThreadA exits the critical
section and executes Monitor.End(MyLockingObject). Correct?
This is an inconsequential distinction in a single thread environment.

I'm not sure what you mean by "single thread environment". Do you mean it
is a single thread because we have a single processor and really only one
thread executes at a time? If so, would it make a difference if we had
multiple processors. It seems to me that the above program would work the
same, only 1 thread would be executing between Enter and Exit, no matter how
many processors or threads were simultaneously executing.
If the object is MyObj, and if your updates are coded like MyObj.x=1 (etc),
then you would do Enter(MyObj) and Exit(MyObj).

Now we are getting to the crux of my problem. Suppose we did Dim
MyLockingObject as New Object and we replaced MyObj with MyLockingObject.
Would it make a difference? As far as I can tell it would not. We just
substituted one key for another, presuming that MyLockingObject was at least
as visible as MyObject.
If you were to code update1
and update2 as methods in MyObj's class, then you would use Enter(Me) and
Exit(Me) in these methods. In this way, you can have multiple instances of
the class operating in a multi-threaded environment without these updates
stepping on eachother.

This is really the same thing because Me really points to MyObj.

Thank you again for your help. I REALLY REALLY REALLY appreciate your time
and effort getting this through my skull.

Clark
 
Monitor - used to lock critical sections of code so only one thread can
So, continuing with my analogy, when the key is used, ALL locks that used
the key are locked at the same time. Sort of a wireless key or something
that can lock several locks at once....

If in several different places in your code you have
Enter(zzz)
' code goes here
Exit(zzz)
Then at execution time, at most one of these blocks can be executing at a
time. If you think about it, you can see generally (not in detail) how .net
achieves this. At program start, no such block is being executed.
Eventually, one gets executed (by thread1 for example), and .net makes a copy
of the zzz object reference to remember that Enter(zzz) has been executed.
Then thread2 tries to do an Enter(zzz), and .net blocks it because it already
has a copy of the zzz object reference. Then thread3 tries to do Enter(zzz)
and .net blocks it too. Then thread1 does Exit(zzz), and .net erases its
copy of zzz, and that makes threads 2 and 3 ready to be dispatched. One of
them will get an execution burst and will Enter(zzz), and the other will
remain blocked. So, your wireless metaphor doesn't really help. What is
more apt is that zzz is Entered or not, or available or not, or the engine is
running or not. Wireless kind of misses the point.
I'm not totally sure what you mean by "on the same object". I think you
mean that several sections of code are locked by the same locking object.

Lets see if I get this or not. Suppose ThreadA is executing some code and
it enters a critical section locked by MyLockingObject. When it gets to
Monitor.Enter(MyLockingObject), MyLockingObject is tweaked in some way we
don't need to understand, and the thread begins executing the critical
section. Now suppose a thread switch occurs and ThreadB is started. It
encounters a Monitor.Enter(MyLockingObject) in a different section of code.
Because that code is locked using MyLockingObject, ThreadB will be blocked
until execution is switched back to ThreadA and ThreadA exits the critical
section and executes Monitor.End(MyLockingObject). Correct?
Yes!


I'm not sure what you mean by "single thread environment". Do you mean it
is a single thread because we have a single processor and really only one
thread executes at a time? If so, would it make a difference if we had
multiple processors. It seems to me that the above program would work the
same, only 1 thread would be executing between Enter and Exit, no matter how
many processors or threads were simultaneously executing.

When you run an executable, you create a process. A process consists of one
or more execution threads. Windows and .net execute a process with some
number of threads on single processor and multi-processor computers without
distinction. Don't let single or multiple processors enter into your
thinking. Enter/Exit and other synchronization devices solve thread
contention problems without regard to how many processors there are. A
contention bug in a multithreaded app may be more easily exposed on a
multi-processor computer, but it is a bug that will eventually show up on a
single processor system.

Windows gives threads time slices according to its dispatching algorithm.
So, if you have two threads, thread1 about to execute x=1:y=2 and thread2
about to execute x=3:y=4, then the execution result is not predictable (two
threads, doesn't matter how many processors). x=1 and y=4 is possible if
thread2 runs first and its time slice ends after x=3. When it runs next,
thread1 will have completed, so we get the result x=1 and y=4. Presumably
this is illogical because no code sequence has tried to achieve this result.
Now we are getting to the crux of my problem. Suppose we did Dim
MyLockingObject as New Object and we replaced MyObj with MyLockingObject.
Would it make a difference? As far as I can tell it would not. We just
substituted one key for another, presuming that MyLockingObject was at least
as visible as MyObject.

Well, yes. But the code is more readable if you lock the object being
manipulated, and threads will block more than necessary. Lets say you have a
global object MyGlobal. You could code Enter(MyGlobal) and Exit(MyGlobal)
for all bodies of code that would suffer from multithread contention of any
kind throughout your program. It would work. But your threads would block
more than is necessary to solve the real contention problems. If you are
going to update properties of an object, then Enter/Exit that object, and
that way other threads not using that particular object will not be blocked.
This is really the same thing because Me really points to MyObj.

Yes for methods in classes - using Enter(Me) and Exit(Me) makes a lot of
sense when you want to make the class safe in a multi-thread environment.
 
Thank you so much. I understand all the points in your last email and, once
again, I thank you for your time. You have done a very good job of
explaining something that has been troubling me for a long time.

Clark
 
Back
Top