Don't starve your Cookie Monster

01-03-2019

Cookie Monster

First of all I want to say a few words about the image above. I bet almost all of you know a Muppet called Cookie Monster. As well as Cookie Monster doesn’t like when there are no cookies, our threads don’t like to be blocked.

Work with threads isn’t simple. That is quite obvious for everyone who tried it. One of a vast number of issues is an exclusive lock of a given resource which provides us thread synchronization.

synchronize

The easiest way to synchronize the access to a resource is to use the lock statement on a guard variable. Just like the example below:

var guard = new object();
lock(guard)
{
    // access the critical path
}

If you think that harmless lock couldn’t be dangerous, you are wrong. This simple and it might seem an easy way to the threads synchronization may end with a DEADLOCK.

deadlock

You may ask how this is possible. To understand the problem, we need to dive deeper and understand how the compiler process the code given above.

var guard = new object();
try
{
    Monitor.Enter(guard);
    // access the critical path
}
finally
{
    Monitor.Exit(guard);
}

The lock statement has been replaced with the Monitor.Enter() method. How does this method work? A thread requests the lock on a resource and waits until the access will be obtained. How this can produce a deadlock. Let’s take a look at this example of two threads synchronization on two guard variables.

Our threads A and B are waiting and waiting until the end of the world just as Cookie Monster is sadly waiting for cookies. waiting

We can prevent this situation by merely using another method from the Monitor *](https://docs.microsoft.com/en-us/dotnet/api/system.threading.monitor?view=netframework-4.7.2) class. The method is named [TryEnter*. Why it’s so special? It could take as an argument the specified amount of time indicating how long the attempts will be repeated. After the given time thread will proceed further — no more deadlocks.

var guard = new object();
try
{
    Monitor.TryEnter(guard, TimeSpan.FromSeconds(15));
    // access the critical path
}
finally
{
    Monitor.Exit(guard);
}

A good practice is to ensure that the lock is released by calling the Monitor.Exit method on given guard.

I know that the lock statement is shorter than using explicit Monitor.TryEnter method, but the effort devoted to writing some extra lines of code could turn to you on a Sunday evening during the production deploy.

To sum up.

Avoid using the lock statement and start using Monitor.TryEnter. Your threads will be grateful.

Thanks

All the gifs came from giphy.com.

Back