loading...
Cover image for Don't starve your Cookie Monster

Don't starve your Cookie Monster

rafalpienkowski profile image Rafal Pienkowski ・3 min read

First of all I want to say a few words about the cover image. 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.

  • Thread A locks guard X
  • Thread B locks guard Y
  • Thread A wants to lock guard Y, but guard Y is locked by thread B
  • Thread B wants to lock guard X, but thread A locks guard X

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 * 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.

Posted on by:

rafalpienkowski profile

Rafal Pienkowski

@rafalpienkowski

I'm focused on developing and expanding my knowledge and skills. Enjoying new challenges. I'm assuming that there are no stupid questions, there are only silly answers.

Discussion

markdown guide
 

Deadlock = Cookie Monster's starving :D - thanks for that idea! It'll be easier to remember the Monitor class and its usage.