May 9, 2008

Monitor and lock

Below is sample code about how to use Monitor.

            MyClass obj;
            //Some code to initialize obj;
            //This section is now thread-safe:
            Monitor.Enter(obj);
            obj.DoSomething(  );
            Monitor.Exit(obj);
            

This is a little bit misleading. Because the point here is not obj.DoSomething(), the point here Monitor.Enter(obj), and Montior.Exit(obj). If another thread call obj.DoSomething at the same time, but not following the Enter/Exit fashion, the obj.DoSomething is not protected at all. The mechanics there is that you can not Call Monitor.Enter(obj) successfully at the same time. If one thread enter the obj, another thread need to wait until the other thread exit the object. Tomake this pattern easy to use, c# introduce lock keyword. if method is static, you can follow the following example

            public class Demolock
            {
            public static void MethodOne()
            {
            lock (typeof(Demolock))
            {
            Console.WriteLine("methodone at " + DateTime.Now.ToString());
            Thread.Sleep(2000);
            }
            }

            public static void MethodTwo()
            {
            lock (typeof(Demolock))
            {
            Console.WriteLine("methodtwo at " + DateTime.Now.ToString());
            Thread.Sleep(2000);
            }
            }
            }
            

If the method is instance method, and you want to protect all the instance method at the same level, you can use this example. In this example, MethodThree of one instance can be called during the MethodOne of the same instance is called, but MethodOne of one instance can not be called during the MethodTwo of the same instance is called.

            public class Demolock
            {

            public void MethodOne()
            {
            lock (this)
            {
            Console.WriteLine("methodone at " + DateTime.Now.ToString());
            Thread.Sleep(2000);
            }
            }

            public void MethodTwo()
            {
            lock (this)
            {
            Console.WriteLine("methodtwo at " + DateTime.Now.ToString());
            Thread.Sleep(2000);
            }
            }

            public void MethodThree()
            {
            Console.WriteLine("methodtwo at " + DateTime.Now.ToString());
            Thread.Sleep(2000);

            }
            }
            

If the different instance methods are independent, you just want to limit protect the entrance of individual method, you need to create different lock object for different method. For example

            public class Demolock
            {
            object objOne = new object();
            object objTwo = new object();

            public void MethodOne()
            {
            lock (objOne)
            {
            Console.WriteLine("methodone at " + DateTime.Now.ToString());
            Thread.Sleep(2000);
            }
            }

            public void MethodTwo()
            {
            lock (objTwo)
            {
            Console.WriteLine("methodtwo at " + DateTime.Now.ToString());
            Thread.Sleep(2000);
            }
            }
            }

            

Because this programming model is so common, .NET has built-in compiler support for it, called synchronized methods. The MethodImpl method attribute, defined in the System.Runtime.CompilerServices namespace, accepts an enum of type MethodImplOptions. One of the enum values is MethodImplOptions.Synchronized. When the MethodImpl method attribute is applied on a method with that enum value, the compiler instructs the .NET runtime to lock the object on method entry and unlock it on exit. This is semantically equivalent to encasing the method code with a lock statement. For example, consider this method definition:

            using System.Runtime.CompilerServices;

            public class MyClass
            {
            [MethodImpl(MethodImplOptions.Synchronized)]
            public void DoSomething(  )
            {
            /* Method code */
            }
            //Class members
            }

            

This method is semantically identical to the following:

            public class MyClass
            {
            public void DoSomething(  )
            {
            lock(this)
            {
            /* Method code */
            }
            }
            //Class members
            }

            using System.Runtime.CompilerServices;

            public class MyClass
            {
            int m_Number = 0;
            int Number
            {
            [MethodImpl(MethodImplOptions.Synchronized)]
            get
            {
            return m_Number;
            }
            [MethodImpl(MethodImplOptions.Synchronized)]
            set
            {
            m_Number = value;
            }
            }
            }
            

For the producer/consumer scenario, after producer produce the result, it should signal consumer to work, after consumer finished, it should signal producer.

            public class Demolock
            {
            public void Produce()
            {
            lock (this)
            {
            for (int i = 0; i < 10; i++)
            {
            Console.WriteLine("Produce at " + DateTime.Now.ToString());
            Monitor.Pulse(this);
            Monitor.Wait(this);
            }
            }
            }

            public void Consume()
            {
            lock (this)
            {
            for (int i = 0; i < 10; i++)
            {
            Console.WriteLine("Consume at " + DateTime.Now.ToString());
            Monitor.Pulse(this);
            Monitor.Wait(this, 1000);
            }
            }
            }
            }