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.