C#
lock statement
reentrant code
multithreading
synchronization

Is the lock statement reentrant in C?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Yes, the C# lock statement is reentrant for the same thread. If a thread already holds a lock on a given object, it can enter another lock on that same object without deadlocking itself. That behavior exists because lock is built on Monitor, which tracks ownership per thread and keeps a recursion count.

What Reentrant Means Here

In the context of locking, reentrant means the thread that already owns the lock may acquire it again. Another thread still has to wait, but the owning thread is allowed to re-enter.

This matters when a method that takes a lock calls another method that takes the same lock object. Without reentrancy, that pattern would deadlock immediately.

Example of Reentrant Locking

csharp
1using System;
2
3public class Counter
4{
5    private readonly object _sync = new object();
6    private int _value;
7
8    public void Increment()
9    {
10        lock (_sync)
11        {
12            Console.WriteLine("Increment entered");
13            IncrementCore();
14        }
15    }
16
17    private void IncrementCore()
18    {
19        lock (_sync)
20        {
21            _value++;
22            Console.WriteLine($"Value is now {_value}");
23        }
24    }
25}

When Increment() calls IncrementCore(), the same thread enters lock (_sync) twice. That works because the monitor is reentrant for the owning thread.

How lock Works Under the Hood

The lock statement compiles to Monitor.Enter and Monitor.Exit in a try and finally shape. Conceptually it looks like this:

csharp
1bool lockTaken = false;
2try
3{
4    System.Threading.Monitor.Enter(_sync, ref lockTaken);
5    // critical section
6}
7finally
8{
9    if (lockTaken)
10    {
11        System.Threading.Monitor.Exit(_sync);
12    }
13}

When the same thread enters the monitor again, the runtime increments the recursion count instead of blocking. The lock is released fully only after the same thread exits the monitor the matching number of times.

Reentrant Does Not Mean Risk-Free

Reentrancy solves one specific problem: self-deadlock on the same lock object. It does not make every locking pattern safe.

For example, this can still deadlock:

csharp
1private readonly object _lockA = new object();
2private readonly object _lockB = new object();
3
4public void Method1()
5{
6    lock (_lockA)
7    {
8        lock (_lockB)
9        {
10        }
11    }
12}
13
14public void Method2()
15{
16    lock (_lockB)
17    {
18        lock (_lockA)
19        {
20        }
21    }
22}

Two threads can deadlock here because reentrancy only helps when the same thread reacquires the same lock. It does nothing for lock-order inversion across multiple locks.

Good Locking Practice Still Matters

Even with a reentrant lock, keep critical sections short and lock on a private dedicated object.

csharp
private readonly object _sync = new object();

Avoid locking on:

  • 'this'
  • string literals
  • type objects such as typeof(MyType)
  • publicly accessible objects

Those patterns make it too easy for unrelated code to lock the same object and create hidden contention or deadlocks.

Common Pitfalls

The biggest mistake is assuming reentrancy makes nested locking harmless in every design. It only prevents self-deadlock on the same monitor for the same thread.

Another issue is forgetting that every successful enter must be matched by an exit. With the lock statement this is handled for you, but manual Monitor.Enter and Monitor.Exit code must preserve that symmetry.

People also often confuse reentrant locking with reentrant code. A reentrant lock allows the same thread to reacquire the lock. Reentrant code is a broader concept about safe re-entry during execution. They are related ideas, but not the same thing.

Finally, do not use reentrancy as an excuse for complex call graphs that keep re-locking the same state from many directions. The code may still work, but it becomes harder to reason about and easier to break later.

Summary

  • The C# lock statement is reentrant for the thread that already owns the lock.
  • It works because lock uses Monitor, which tracks recursive entry count per thread.
  • Reentrancy prevents self-deadlock when one locked method calls another method that locks the same object.
  • It does not protect you from deadlocks involving multiple lock objects or multiple threads.
  • Keep locking disciplined: use private lock objects and keep critical sections small.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.