Thread Locking In C#

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 is used to lock a resource. The syntax for the lock statement is as follows:

lock (object resource) {
  // Code that accesses the resource
}

The resource parameter can be any object, but it is typically a reference to the shared resource. When the lock statement is executed, the current thread acquires a lock on the resource. Any other threads that try to acquire a lock on the resource will be blocked until the current thread releases the lock.

The lock statement is typically used to protect critical sections of code, which are blocks of code that access and modify shared resources.

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.

using System;  
using System.Threading;  
using System.Diagnostics;  
namespace Threadlocking  
{  
    class Department  
    {  
        private Object thisLock = new Object();  
        int salary;  
        Random r = new Random();  
        public Department(int initial)  
        {  
            salary = initial;  
        }  
        int Withdraw(int amount)  
        {  
            // This condition never is true unless the lock statement  
            // is commented out.  
            if (salary < 0)  
            {  
                throw new Exception("Negative Balance");  
            }  
            // Comment out the next line to see the effect of leaving out   
            // the lock keyword.  
            lock (thisLock)  
            {  
                if (salary >= amount)  
                {  
                    Console.WriteLine("salary before Withdrawal :  " + salary);  
                    Console.WriteLine("Amount to Withdraw        : -" + amount);  
                    salary = salary - amount;  
                    Console.WriteLine("salary after Withdrawal  :  " + salary);  
                    return amount;  
                }  
                else  
                {  
                    return 0; // transaction rejected  
                }  
            }  
        }  
        public void DoTransactions()  
        {  
            for (int i = 0; i < 100; i++)  
            {  
  
                Withdraw(r.Next(1, 100));  
            }  
        }  
    }  
    class Process  
    {  
        static void Main()  
        {  
            Thread[] threads = new Thread[10];  
            Department dep = new Department(1000);   
            for (int i = 0; i < 10; i++)  
            {  
                Thread t = new Thread(new ThreadStart(dep.DoTransactions));  
                threads[i] = t;  
            }  
            for (int i = 0; i < 10; i++)  
            {  
                threads[i].Start();  
            }  
            Console.Read();  
        }  
    }  
}

Output

lock.gif

In general, we avoid locking because lock (this)is a problem if the instance can be accessed publicly.

C# Nested Locking

A thread can repeatedly lock the same object in a nested (reentrant) fashion:

lock (locker)  
lock (locker)  
lock (locker)  
{
    // Do something...  
}

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:

using System;  
using System.Threading;  
using System.Diagnostics;  
namespace nestedlocking  
{  
    class Program  
    {  
        static object x = new object();  
        static void AnotherMethod()  
        {  
            lock (x) { Console.WriteLine("Another method"); }  
        }  
        static void Main()  
        {  
            lock (x)  
            {  
                AnotherMethod();  
                // We still have the lock - because locks are reentrant.  
            }  
            Console.Read();  
        }  
    }  
}

Output

nestloc.gif

The problem with C# 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.


Similar Articles