BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Parallel and Asynchronous
.NET 1.1+

Locking for Thread Synchronisation

Using multi-threading or parallel programming techniques can improve the performance and responsiveness of software but introduces the risk of race conditions that can cause intermittent bugs. These can be mitigated with appropriate locking.

Deadlocks

The careful use of locking can address the problem of race conditions. However, it is possible to overuse locks or apply them incorrectly. One problem with adding unnecessary lock statements is that they can impact the performance of your application. The locking itself is a very small overhead but blocking threads unnecessarily can cause large delays. A bigger problem than the performance issue is that of deadlocking.

A deadlock occurs when two or more threads are blocked by locks that can never be released. The simplest deadlock occurs when you have two threads where each requires locks for two critical sections of code. When each of the threads has obtained one of the desired locks and requested the other, each prevents the other from completing. This can cause your software to become unresponsive until the application is forcibly closed by the user.

Deadlocking is not limited to pairs of threads. You can have many more threads, each competing for locks, that block each other permanently. You should take special care with your designs to ensure that this cannot happen.

The final sample demonstrates a deadlock with a rather contrived example. In this code two parallel tasks are started. In Task1, a lock is obtained using the _lock1 object. The thread then pauses for a couple of seconds before attempting to lock on the _lock2 object.

Task2 performs similar actions but using the lock objects in the reverse order. It obtains a lock using _lock2 before pausing. It then attempts to create a lock using the _lock1 variable. By this time, Task1 has obtained this lock, blocking Task2. However, Task1 needs a lock on _lock1, which is held by Task2. The tasks are therefore deadlocked and the program never exits.

class Program
{
    static object _lock1 = new object();
    static object _lock2 = new object();

    static void Main(string[] args)
    {
        Parallel.Invoke(Task1, Task2);
    }

    static void Task1()
    {
        lock (_lock1)
        {
            Console.WriteLine("Task1 locked on _lock1");
            Thread.Sleep(2000);
            lock (_lock2)
            {
                Console.WriteLine("Task1 locked on _lock2");
            }
        }
    }

    static void Task2()
    {
        lock (_lock2)
        {
            Console.WriteLine("Task2 locked on _lock2");
            Thread.Sleep(2000);
            lock (_lock1)
            {
                Console.WriteLine("Task2 locked on _lock1");
            }
        }
    }
}
27 September 2012