Thread Locking In C#

Thread Locking

 
Exclusive locking in threading ensures that one thread does not enter a critical section while another thread is in the critical section of code. If another thread attempts to enter a locked code, it will wait (block) until the object is released. To achieve this functionality we have to main exclusive locking constructs are,
  • Mutex
  • Lock
The lock construct is faster and more convenient. Mutex, though, has a place in that its lock can span applications in different processes on the computer.
 
The lock statement takes the following form,
 
lock(expression) statement_block
 

Where

 
expression
 
Specifies the object that you want to lock on. expression must be a reference type.

statement_block
 
The statements of the critical section. Acritical section is a piece of code that accesses a shared resource (data structure or device) but the condition is that only one thread can enter in this section in a time.
 
In the following example we implement threads andlock. As long as thelockstatement is present, the statement block is a critical section andSalarywill never become a negative number.
  1. using System;  
  2. using System.Threading;  
  3. using System.Diagnostics;  
  4. namespace Threadlocking  
  5. {  
  6. class Department  
  7.     {  
  8.         private Object thisLock = new Object();  
  9.         int salary;  
  10.         Random r = new Random();  
  11.         public Department(int initial)  
  12.         {  
  13.             salary = initial;  
  14.         }  
  15.         int Withdraw(int amount)  
  16.         {  
  17.             // This condition never is true unless the lock statement  
  18.             // is commented out.  
  19.             if (salary < 0)  
  20.             {  
  21.                 throw new Exception("Negative Balance");  
  22.             }  
  23.             // Comment out the next line to see the effect of leaving out   
  24.             // the lock keyword.  
  25.             lock (thisLock)  
  26.             {  
  27.                 if (salary >= amount)  
  28.                 {  
  29.                     Console.WriteLine("salary before Withdrawal :  " + salary);  
  30.                     Console.WriteLine("Amount to Withdraw        : -" + amount);  
  31.                     salary = salary - amount;  
  32.                     Console.WriteLine("salary after Withdrawal  :  " + salary);  
  33.                     return amount;  
  34.                 }  
  35.                 else  
  36.                 {  
  37.                     return 0; // transaction rejected  
  38.                 }  
  39.             }  
  40.         }  
  41.         public void DoTransactions()  
  42.         {  
  43.             for (int i = 0; i < 100; i++)  
  44.             {  
  45.   
  46.                 Withdraw(r.Next(1, 100));  
  47.             }  
  48.         }  
  49.     }  
  50. class Process  
  51.     {  
  52.         static void Main()  
  53.         {  
  54.             Thread[] threads = new Thread[10];  
  55.             Department dep = new Department(1000);   
  56.            for (int i = 0; i < 10; i++)  
  57.             {  
  58.                Thread t = new Thread(new ThreadStart(dep.DoTransactions));  
  59.                 threads[i] = t;  
  60.             }  
  61.             for (int i = 0; i < 10; i++)  
  62.             {  
  63.                 threads[i].Start();  
  64.             }  
  65.             Console.Read();  
  66.         }  
  67.     }  
  68. }  
Output
 
lock.gif
 
In general, we avoid locking because lock (this)is a problem if the instance can be accessed publicly.
 

Nested Locking

 
A thread can repeatedly lock the same object in a nested (reentrant) fashion:
  1. lock (locker)  
  2. lock (locker)  
  3. lock (locker)  
  4. {// Do something...  
  5. }  
In the nested locking, the object is unlocked only when the outermost lock statement has exited. It is useful when one method calls another within a lock:
  1. using System;  
  2. using System.Threading;  
  3. using System.Diagnostics;  
  4. namespace nestedlocking  
  5. {  
  6. class Program  
  7.     {  
  8.         static object x = new object();  
  9.         static void AnotherMethod()  
  10.         {  
  11.             lock (x) { Console.WriteLine("Another method"); }  
  12.         }  
  13.         static void Main()  
  14.         {  
  15.             lock (x)  
  16.             {  
  17.                 AnotherMethod();  
  18.                 // We still have the lock - because locks are reentrant.  
  19.             }  
  20.             Console.Read();  
  21.         }  
  22.     }  
  23. }  
Output
 
nestloc.gif
 

The problem with threading

 
Lock-based resource protection and thread/process synchronization have many disadvantages,
  • They cause blocking, which means some threads/processes have to wait until a lock (or a whole set of locks) is released.
  • Lock handling adds overhead for each access to a resource, even when the chances for collision are very rare. (However, any chance for such collisions is a race condition).
  • Lock contention limits scalability and adds complexity.
  • Priority Inversion High priority threads/processes cannot proceed, if a low priority thread/process is holding the common lock.
  • Convoying. All other threads have to wait, if a thread holding a lock is descheduled due to a time-slice interrupt or page fault.
  • Hard to debug: Bugs associated with locks are time dependent. They are extremely hard to replicate.