Now, what if Monitor.Enter were to throw an exception? You really need to bring this call inside the try block. But then you need to cater for two possibilities: the exception happening before the monitor was acquired and the exception being thrown after the monitor was acquired. So you need to have some way of telling whether or not you should release the monitor. Fortunately, in version 4.0 of .NET a new overload was introduced that allows users to verify, one way or the other, whether the lock was taken. Listing 4-8 shows the code necessary to use this new overload.
Listing 4-8. Monitor.Enter Inside the try Block
public void ReceivePayment(decimal amount)
{
bool lockTaken = false;
try
{
Monitor.Enter(stateGuard, ref lockTaken);
cash += amount;
receivables -= amount;
}
finally
{
if (lockTaken)
{
Monitor.Exit(stateGuard);
}
}
}
If this is the code you should write to use monitors correctly, you have a problem: the chances of getting developers to write this every time and get it right every time are not great. And so the C# language has a keyword that makes the compiler emit the code in Listing 4-8.
The lock Keyword
The idea of the lock keyword is to allow you to concentrate on the work that needs to be protected rather than the semantics of using monitors correctly. Listing 4-9 shows how the lock keyword makes the code in Listing 4-8 much simpler. This is surely something you can expect developers to write.
Listing 4-9. Using the lock Keyword
public void ReceivePayment(decimal amount)
{
lock(stateGuard)
{
cash += amount;
receivables -= amount;
}
}
WHAT SHOULD I LOCK?
In the early days of .NET it was very common to see people write code like the following:
lock(this)
{
// change state here
}
In fact, this is also what the event keyword generated until .NET 4.0. The problem is that an object’s this reference is really a public field, in that anyone with a reference to the object is looking at the same thing. This means that, although you are using this for your own internal synchronization, other code may also choose your object for synchronization. You then end up in needless contention at best and with hard-to-diagnose deadlocks at worst.
Objects are cheap to allocate, and so the simplest way to ensure you only have contention where necessary is to use private instance or static variables, depending on whether the state to be protected is instance or static data, respectively.
Even though the lock keyword appears to be a big improvement on manually manipulating the monitor, there is still a fundamental problem: when trying to acquire synchronization primitives it’s a very good idea to be able to time out of a wait. Failing to do this can result in hard-to-identify deadlocks and other synchronization bugs (such as a thread failing to release a monitor). The problem is that neither Monitor.Enter nor lock supports the passing of a timeout.
From “Pro asynchronous programming with .net, page 67”.