lock inside lock

I'm wondering if this construction will cause an error:

lock(sync)
{
  // something
  lock(sync)
  {
    //something
    lock(sync)
    {
      //something
    }
  }
}

I've run this code, and it seems fine, but maybe in some circumstances an error may be thrown?


Solution 1:

lock is a wrapper for Monitor.Enter and Monitor.Exit:

The lock keyword calls Enter at the start of the block and Exit at the end of the block. From the former's documentation:

From the documentation for Monitor.Enter:

It is legal for the same thread to invoke Enter more than once without it blocking; however, an equal number of Exit calls must be invoked before other threads waiting on the object will unblock.

Because the calls to Enter and Exit are paired, your code pattern has well defined behaviour.

Note, however, that lock is not guaranteed to be an exception-less construct:

A ThreadInterruptedException is thrown if Interrupt interrupts a thread that is waiting to enter a lock statement.

Solution 2:

To explain why it is well-defined behavior and will never fail:

Aside: This answer has better details about how locks actually work

The lock occurs at the Thread level, so calling it a second time on the same thread will be redundant. I would think it would not have any performance penalty (although that would depend on how exactly the internals of .Net are written, so I can't guarantee that)

Many times you'd have a public function that calls another public function in your class, whom both need the lock when used seperately. If this was not allowed the following would fail:

private Dictionary<string, int> database = new Dictionary<string, int>();
private object databaseLock = new object();
public void AddOrUpdate(string item)
{
    lock (databaseLock)
    {
        if (Exists(item))
            database.Add(item, 1);
        else
            ++database[item];
    }
}
public bool Exists(string item)
{
    lock (databaseLock)
    {
        //... Maybe some pre-processing of the key or item...
        return database.ContainsKey(item);
    }
}