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);
    }
  }
}

      

Console output looks like some threads have passed the monitor ?? http://img26.imageshack.us/img26/3633/outputs.jpg

+1


a source to share


3 answers


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?

+4


a source


'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.

+2


a source


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 object x

    .
  • T2

    trying to acquire a lock on an object x

    . This thread is out of luck as we know it will wait forever.
  • T1

    changes x

    . This is now a new object. I will name it x'1

    .
  • T3

    trying to acquire the lock. But the variable x

    actually refers to the object x'1

    . No one is locked on x'1

    , so it passes.
  • T3

    changes x

    . This is now a new object named x'3

    .
  • T1

    is written to the console.
  • T3

    is written to the console.
  • T1

    trying to release the lock variable x

    , 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
+1


a source







All Articles