Myster
Myster

Reputation: 18114

Why do nested locks not cause a deadlock?

Why does this code not cause a deadlock?

   private static readonly object a = new object();

...

   lock(a)
   {
      lock(a)
      {
         ....
      }
   }

Note: for a more advanced discussion, see the question Is the lock statement reentrant in C#? which asks a yes/no question, this question asks a 'why' question. This is a beginner question, and more suitable for someone unfamiliar with the topic.

Upvotes: 63

Views: 21555

Answers (3)

Dan J
Dan J

Reputation: 16718

From section 8.12 of the C# language specification:

While a mutual-exclusion lock is held, code executing in the same execution thread can also obtain and release the lock. However, code executing in other threads is blocked from obtaining the lock until the lock is released.

It should be obvious that the internal lock scope is in the same thread as the outer.

Upvotes: 12

Anon.
Anon.

Reputation: 60033

If a thread already holds a lock, then it can "take that lock" again without issue.


As to why that is, (and why it's a good idea), consider the following situation, where we have a defined lock ordering elsewhere in the program of a -> b:

void f()
{
    lock(a)
    { /* do stuff inside a */ }
}

void doStuff()
{
    lock(b)
    {
        //do stuff inside b, that involves leaving b in an inconsistent state
        f();
        //do more stuff inside b so that its consistent again
    }
}

Whoops, we just violated our lock ordering and have a potential deadlock on our hands.

We really need to be able to do the following:

function doStuff()
{
    lock(a)
    lock(b)
    {
        //do stuff inside b, that involves leaving b in an inconsistent state
        f();
        //do more stuff inside b so that its consistent again
    }
}

So that our lock ordering is maintained, without self-deadlocking when we call f().

Upvotes: 59

David Ly
David Ly

Reputation: 31606

The lock keyword uses a re-entrant lock, meaning the current thread already has the lock so it doesn't try to reacquire it.

A deadlock occurs if

Thread 1 acquires lock A
Thread 2 acquires lock B
Thread 1 tries to acquire lock B (waits for Thread 2 to be done with it) Thread 2 tries to acquire lock A (waits for Thread 1 to be done with it)

Both threads are now waiting on each other and thus deadlocked.

Upvotes: 27

Related Questions