Strange behavior when changing an exclusively locked object - Monitor.Enter (x)
I wanted to know what happened if you changed the reference of an object exclusively blocked by Monitor.Enter (). As expected, a SynchronizationLockException was thrown. But I was surprised to see several threads passing by the Monitor before the exception was thrown.
This is what the code below does.
- create and run 100 threads
- make the whole stream until ManualResetEvent is set.
- set ManualResetEvent - Kinda like waving green flag in Indy race.
- set an exclusive lock (Monitor.Enter (x)) on x
- change x link.
At this point, I was expecting some kind of exception to happen, but this will not happen until Monitor.Exit (x). What's really weird is that 10-20 threads seem to be able to get past the lock before the exception is raised. How does this happen? It seems it shouldn't. Of course, changing the reference of an exclusively locked object is no-no. I would never do this in real code, but still I was surprised to see other threads passing by the monitor. Your thoughts?
using System;
using System.Threading;
namespace ThreadingPlayground
{
class Program
{
private int _value;
object x = new object();
ManualResetEvent _event = new ManualResetEvent(false);
static void Main()
{
Program p = new Program();
p.StartThreads();
Console.ReadLine();
}
private void StartThreads()
{
for(int i = 0;i<100;i++)
{
Thread t = new Thread(IncrementValue);
t.Start();
}
_event.Set();
}
private void IncrementValue()
{
WaitHandle.WaitAll(new WaitHandle[] {_event});
Monitor.Enter(x);
// Change the reference of the exclusively locked object. This
// allows other threads to enter, should this be possible?
x = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine(++_value);
// throws a SynchronizationLockException
// but not until 10 - 20 more lines are written
Monitor.Exit(x);
}
}
}
a source to share
What you see is the expected behavior. There is nothing special about the actual variable used to pass the reference to Monitor.Enter()
. Changing the link should not prevent other threads from acquiring an exclusive lock, since the variable has a new value and this link is not locked anywhere.
Your problem is related to Exit
because the caller thread Exit
does not have an exclusive lock on the passed link. There may be a blocking in another thread, but the thread you are executing in does not.
This is, as you know, so it is always best to do your lock on a variable whose reference will never change. If your resource variable can change, use a new link.
Is this clear enough?
a source to share
'x' - object reference; it is not the object itself. When you do
x = Thread.CurrentThread.ManagedThreadId.ToString();
You threw away the locked object you mentioned earlier and made x a reference to some other object. Now that you do
Monitor.Exit(x);
You get an exception because this object is not actually locked - the object you locked is now garbage collected by the garbage collector.
a source to share
The reason for this behavior is that you are changing the value of x here:
x = Thread.CurrentThread.ManagedThreadId.ToString();
So when you get to
Monitor.Exit(x)
you release the lock with another object. It's like putting a lock with one key and trying to remove the lock with a key from another lock.
Also, Console.Writeline is an expensive instruction compared to others, so multiple threads access the monitor before one of them gets close to the finish line.
Here's an example:
You start a chain of threads.
- The topic
T1
acquires a lock with the objectx
. -
T2
trying to acquire a lock on an objectx
. This thread is out of luck as we know it will wait forever. -
T1
changesx
. This is now a new object. I will name itx'1
. -
T3
trying to acquire the lock. But the variablex
actually refers to the objectx'1
. No one is locked onx'1
, so it passes. -
T3
changesx
. This is now a new object namedx'3
. -
T1
is written to the console. -
T3
is written to the console. -
T1
trying to release the lock variablex
, which points to an object ...x'3
. - Subject
Monitor
says, "Hey, what do you think you're doing? You don't have this lock! Eat this Exception little sucker." - Fin
a source to share