62

Why does this code not cause a deadlock?

   private static readonly object a = new object();

...

   lock(a)
   {
      lock(a)
      {
         ....
      }
   }
Cœur
  • 37,241
  • 25
  • 195
  • 267
Myster
  • 17,704
  • 13
  • 64
  • 93
  • Why are you expecting it to cause a deadlock? (also, this is fairly similar to http://stackoverflow.com/questions/391913/re-entrant-locks-in-c which may help) – AakashM Feb 17 '11 at 21:53
  • 4
    This is clearly a duplicate of that, but this question's title is infinitely better (for people that would likely be seeking it). – Jeff Sternal Feb 17 '11 at 21:57

3 Answers3

58

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().

Anon.
  • 58,739
  • 8
  • 81
  • 86
  • 1
    Can you explain why there is a deadlock? Your lock ordering seems consistent to me. All code paths lock A OR B->A there is no code path to lock A->B – Jared Kells Aug 12 '14 at 03:15
  • 1
    @JaredKells It's defined just before the first code block; .... where we have a defined lock ordering **elsewhere in the program** of a -> b: – Mr47 Apr 23 '15 at 09:57
25

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.

Davy8
  • 30,868
  • 25
  • 115
  • 173
  • Trying to find an example of a non-reentrant lock, but Google is failing me. – Davy8 Feb 17 '11 at 22:03
  • 1
    [ReaderWriterLockSlim](http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim%28v=vs.110%29.aspx) is an example. – Chuu Nov 13 '14 at 18:54
12

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.

Dan J
  • 16,319
  • 7
  • 50
  • 82