C# Monitor – Some Important Points

Aug 1st, 2010 | By | Category: Programming & Languages

In my last post I started the discussion on race conditions and thread synchronization technique using Monitor/lock in C#.In this post also, we will continue with the same and discuss some important points related to the Monitor class.

Value Types

The lock statement or the Monitor.Enter method expects a parameter of type object on which the lock is held.So what happens if we pass value type as parameter?

private int i;

public void M2()
{
    lock (i)
    {
        Console.WriteLine("Method::M2::Entered Critical Section");
    }
} 

This will throw a compilation error CS0185: ‘int’ is not a reference type as required by the lock statement.

So the natural question is why C# compiler is not allowing us to pass a value type as parameter? This is because when we pass a value type it will be boxed to a reference type i.e. object in this case.So every time the lock statement is executed, boxing happens and we get a completely new and separate object instance.So all the executing threads will be able to acquire the lock as the object itself will be a different one.

Re-entrancy

The locking provided by Monitor/lock statement are reentrant in nature i.e. the same thread can acquire the lock on same object multiple times.

public class A
{
    private object objLock = new object();
    private int i;
    public void M1()
    {
        lock (objLock)
        {
            Console.WriteLine("Method::M1::Entered Critical Section");
            M2();
        }
    }
    public void M2()
    {
        lock (objLock)
        {
            Console.WriteLine("Method::M2::Entered Critical Section");
        }
    }
}

This code will produce the output as:

Method::M1::Entered Critical Section
Method::M2::Entered Critical Section

This is because lock statement allows the same thread to acquire lock on objLock multiple times once within method M1 and then within M2.

Lock On Public Types/Objects

As per MSDN

In general, avoid locking on a public type, or instances beyond your code’s control. The common constructs lock (this), lock (typeof (MyType)), and lock (“myLock”) violate this guideline:

  • lock (this) is a problem if the instance can be accessed publicly.
  • lock (typeof (MyType)) is a problem if MyType is publicly accessible.
  • lock(“myLock”) is a problem since any other code in the process using the same string, will share the same lock.

This is very valid guideline I will just try to explain the possible hazards that might arise on using lock on publicly accessible properties/types.Consider the following simple class:

public class Foo
{
    public void Bar()
    {
        lock (this)
        {
            Console.WriteLine("Class:Foo:Method:Bar");
        }
    }
}

Here we are using a lock on the object instance itself.Let’s assume that this code is part of some library/API where the client code is not supposed to be aware of internal implementation.The client code makes a call to Foo/Bar in the following way:

public class MyClient
{
    public void Test()
    {
        Foo f = new Foo();
        lock (f)
        {
            ThreadStart ts = new ThreadStart(f.Bar);
            Thread t = new Thread(ts);
            t.Start();
            t.Join();
        }
    }
}

This program will simply hang because the main thread acquires a lock on instance of Foo and then invokes the method Bar on the same instance f but on a different thread.The method Bar also tries to acquire a lock on this i.e instance of Foo (same as f) and waits forever.

So to be on the safe side and write bug free code we should always avoid locking on publicly exposed types and properties.In the earlier post we have seen that we can use the System.Runtime.CompilerServices.MethodImplAttribute to synchronize a method.But this will lead to a lock on the object instance (this) or the Type instance of the class(in case of static methods).So it’s better to avoid this.


Kick It on DotNetKicks.com
Tags: , ,