+3  A: 

What you're seeing is expected behavior. There is nothing special about the actual variable used to pass a reference into Monitor.Enter(). Changing the reference should not prevent other threads from acquiring an exclusive lock, as the variable has a new value, and that reference is not locked anywhere.

Your issue comes with the Exit, because the thread calling Exit does not have an exclusive lock on the reference being passed in. Another thread may well have a lock on it, but the thread you're executing in does not.

This, as you know, is why it's always best to do your locking with a variable whose reference will never change. If your resource's variable might change, use a new reference.

Is this clear enough?

Adam Robinson
So changing the reference of the variable passed to Monitor.Enter() is like releasing the lock. Well, except than an exception will be thrown at Monitor.End. That makes sense. Thank you. Not sure why, but I kinda thought an exception would be thrown as soon as I changed the reference to the locked variable.
Matthew Sposato
No, you're not releasing the lock. Think of it like this: You locked the door on your house, then you had some carpenters come in and _tear out and replace the entire door_. Then you act surprised when the new door doesn't come locked :)
bdonlan
Behaiorally perhaps it's like that, but it isn't actually doing that; it's just changing the reference of the object that other threads will try to lock on to.
Adam Robinson
The other threads decide which object to lock by walking up to the door frame and locking whatever's there. I don't see any problem with the analogy :)
bdonlan
@bdonlan: My comment was actually directed at Matthew. Yours wasn't there when I posted. Yes, your analogy is appropriate :)
Adam Robinson
+2  A: 

'x' is a reference to an object; it is not the object itself. When you do

      x = Thread.CurrentThread.ManagedThreadId.ToString();

You have thrown away the locked object that x previously referred to, and made x refer to some other object. Now when you do

      Monitor.Exit(x);

You get the exception because this object is not in fact locked - the object that you locked is now garbage to be collected by the garbage collector.

bdonlan
Actually, I wouldn't be so quick to say that it'll be collected, as the Monitor class may have a reference to it somewhere (actually, I'd bet money that it does). Nonetheless, your answer is otherwise correct.
Adam Robinson
+1  A: 

The reason for this behavior is that you're changing the value of x here:

x = Thread.CurrentThread.ManagedThreadId.ToString();

So when you get to

Monitor.Exit(x)

you're releasing the lock with a different object. It's as if you put a padlock with one key, and try to remove the padlock with the key from another padlock.

In addition, Console.Writeline is a costly instruction compared to the others, so several threads get to enter the Monitor before one of them gets near the finish line.

Here's an example run:

You start a bunch of threads.

  • Thread T1 acquires the lock with object x.
  • T2 tries to acquire lock with object x. This thread is out of luck, as we know it's going to wait forever.
  • T1 changes x. It's now a new object. I'll call it x'1.
  • T3 tries to acquire the lock. But the variable x actually references the object x'1. Nobody has locked on x'1, so he passes.
  • T3 changes x. It's now a new object called x'3.
  • T1 writes to the console.
  • T3 writes to the console.
  • T1 tries to release the lock with the variable x, which points to the object... x'3.
  • The Monitor object says: "Hey, what do you think you're doing? You don't have that lock! Eat this Exception little sucker"
  • Fin
DonkeyMaster