(ARTICLE) Use lock() As Your First Choice for Synchronization

(ARTICLE) Use lock() As Your First Choice for Synchronization

Bill Wagner shows how to provide a safe way for different threads in your application to send and receive data with each other by using synchronization primitives to protect access to the shared data.

Threads need to communicate with each other. Somehow, you need to provide a safe way for different threads in your application to send and receive data with each other. However, sharing data between threads introduces the potential for data integrity errors in form of synchronization issues. Somehow you need to be certain that the current state of every shared data item is consistent. You achieve this safety by using synchronization primitives to protect access to the shared data. Synchronization primitives ensure that the current thread will not be interrupted until a critical set of operations is completed.

There are many primitives available in the .NET BCL that you can use to safely ensure that access to shared data is synchronized. Only one of them, Monitor.Enter() / Monitor.Exit(), was given special status in the C# language. Monitor.Enter() and Monitor.Exit() implement a critical section block. Critical sections are such a common synchronization technique that the language designers added support for them using the lock() statement. You should follow that example and make lock() the primary tool for synchronization.

The reason is simple: The compiler generates consistent code, and you may make mistakes some of the time. The C# language introduces the lock keyword to control synchronization for multi-threaded programs. The lock statement generates exactly the same code as if you used Monitor.Enter( ) and Monitor.Exit( ) correctly. It’s just easier and it automatically generates all the exception safe code you need.

However, there are two conditions where Monitor gives you necessary control that you can’t get when you use lock(). lock is lexically scoped. That means you can’t enter a Monitor in one lexical scope and exit it in another when using the lock statement. You wouldn’t be able to enter a monitor in a method and exit it inside a lambda expression defined in that method. (See Item 37). The second reason is that Monitor.Enter supports a timeout, which I’ll cover later in this item.

You can lock any reference type with the lock statement:

public int TotalValue
{
    get 
    { 
        lock(syncHandle)
        { 
            return total;
        }
    }
}

public void IncrementTotal()
{
    lock (syncHandle)
    {
        total++;
    }
}

The lock statement gets the exclusive monitor for an object and ensures that no other thread can access the object until the lock is released. The code above using lock() generates the same IL as the following version using Monitor.Enter() and Monitor.Exit():

public void IncrementTotal()
{
    object tmpObject = synchHandle;
    System.Threading.Monitor.Enter(tmpObject);
    try
    {
        total++;
    }
    finally
    {
        System.Threading.Monitor.Exit(tmpObject);
    }
}

The lock statement provides many checks that will help you avoid common mistakes. lock checks that the type being locked is a reference type, as opposed to a value type. Monitor.Enter does not include such safeguards. This routine, using lock() won’t compile:

[READ MORE..]

Courtesy: www.informit.com

Google