Wednesday, April 22, 2009

Lock

Lock actually is the short for Monitor.Enter and Monitor.Exit

Here is the code

class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 100; i++)
            {
                new Thread(Go).Start();
            }
            Thread.Sleep(1000);
            Console.Read();
        }
        static int val1 = 90, val2 = 10;
 
        static object locker = new object();
        static void Go()
        {
            lock (locker)
            {
                if (val2 != 0)
                {
                    Thread.Sleep(20);
                    Console.WriteLine(val1 / val2);
                    val2 = 0;
                }
                else
                {
                    Console.WriteLine("attempted to divide by zero");
                }
            }
                
            }
        
    }

lets look what happens when you start 100 threads of Go function starts and if you don’t lock gives a attempted to divide by zero exception.When you lock problem is solved, lets look at the il code:

.method private hidebysig static void  Go() cil managed
{
  // Code size       88 (0x58)
  .maxstack  2
  .locals init ([0] object CS$2$0000,
           [1] bool CS$4$0001)
  IL_0000:  nop
  IL_0001:  ldsfld     object ConsoleApplication12.Program::locker
  IL_0006:  dup
  IL_0007:  stloc.0
  IL_0008:  call       void [mscorlib]System.Threading.Monitor::Enter(object)
  IL_000d:  nop
  .try
  {
    IL_000e:  nop
    IL_000f:  ldsfld     int32 ConsoleApplication12.Program::val2
    IL_0014:  ldc.i4.0
    IL_0015:  ceq
    IL_0017:  stloc.1
    IL_0018:  ldloc.1
    IL_0019:  brtrue.s   IL_003e
    IL_001b:  nop
    IL_001c:  ldc.i4.s   20
    IL_001e:  call       void [mscorlib]System.Threading.Thread::Sleep(int32)
    IL_0023:  nop
    IL_0024:  ldsfld     int32 ConsoleApplication12.Program::val1
    IL_0029:  ldsfld     int32 ConsoleApplication12.Program::val2
    IL_002e:  div
    IL_002f:  call       void [mscorlib]System.Console::WriteLine(int32)
    IL_0034:  nop
    IL_0035:  ldc.i4.0
    IL_0036:  stsfld     int32 ConsoleApplication12.Program::val2
    IL_003b:  nop
    IL_003c:  br.s       IL_004b
    IL_003e:  nop
    IL_003f:  ldstr      "attempted to divide by zero"
    IL_0044:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_0049:  nop
    IL_004a:  nop
    IL_004b:  nop
    IL_004c:  leave.s    IL_0056
  }  // end .try
  finally
  {
    IL_004e:  ldloc.0
    IL_004f:  call       void [mscorlib]System.Threading.Monitor::Exit(object)
    IL_0054:  nop
    IL_0055:  endfinally
  }  // end handler
  IL_0056:  nop
  IL_0057:  ret
} // end of method Program::Go
 

As you can see it is a try finally block and monitor.enter and monitor.exit methods.If  you replace lock with monitor codes given below il code will be same

 
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 100; i++)
            {
                new Thread(Go).Start();
            }
            Thread.Sleep(1000);
            Console.Read();
        }
        static int val1 = 90, val2 = 10;
 
        static object locker = new object();
        static void Go()
        {
            Monitor.Enter(locker);
            try
            {
                if (val2 != 0)
                {
                    Thread.Sleep(20);
                    Console.WriteLine(val1 / val2);
                    val2 = 0;
                }
                else
                {
                    Console.WriteLine("attempted to divide by zero");
                }
            }
            finally
            {
                Monitor.Exit(locker);
            }
 
 
 
        }
 
    }
 

lock(this) is not a good programming example i will tell more about it another time but for now don’t use it. You must lock a reference variable (please remember structs are value and classes are reference types),

No comments: