What is the difference between Monitor and Lock in C#?

Both Monitor and lock provide thread synchronization mechanisms in C#, but they serve different purposes. The lock statement is a simplified syntax that internally uses Monitor.Enter and Monitor.Exit with proper exception handling. Monitor offers more advanced features for complex threading scenarios.

Syntax

Following is the syntax for using lock statement −

lock (lockObject) {
    // critical section code
}

Following is the syntax for using Monitor class −

Monitor.Enter(lockObject);
try {
    // critical section code
} finally {
    Monitor.Exit(lockObject);
}

Monitor vs Lock Relationship lock Statement Simplified syntax Compiler generates Monitor calls Monitor Class Advanced control TryEnter, Wait, Pulse methods compiles to lock is syntactic sugar for Monitor with automatic exception handling

Using Lock Statement

The lock statement provides a simple way to ensure thread safety for basic synchronization scenarios −

using System;
using System.Threading.Tasks;

class Program {
    static object _lock = new object();
    static int Total = 0;
    
    public static void Main() {
        Task[] tasks = new Task[5];
        
        for (int i = 0; i < 5; i++) {
            tasks[i] = Task.Run(() => AddOneHundredLock());
        }
        
        Task.WaitAll(tasks);
        Console.WriteLine("Final Total using lock: " + Total);
    }
    
    public static void AddOneHundredLock() {
        for (int i = 1; i <= 100; i++) {
            lock (_lock) {
                Total++;
            }
        }
    }
}

The output of the above code is −

Final Total using lock: 500

Using Monitor Class

The Monitor class provides the same functionality as lock but with explicit control over the synchronization mechanism −

using System;
using System.Threading;
using System.Threading.Tasks;

class Program {
    static object _lock = new object();
    static int Total = 0;
    
    public static void Main() {
        Task[] tasks = new Task[5];
        
        for (int i = 0; i < 5; i++) {
            tasks[i] = Task.Run(() => AddOneHundredMonitor());
        }
        
        Task.WaitAll(tasks);
        Console.WriteLine("Final Total using Monitor: " + Total);
    }
    
    public static void AddOneHundredMonitor() {
        for (int i = 1; i <= 100; i++) {
            Monitor.Enter(_lock);
            try {
                Total++;
            } finally {
                Monitor.Exit(_lock);
            }
        }
    }
}

The output of the above code is −

Final Total using Monitor: 500

Advanced Monitor Features

The Monitor class provides additional methods for advanced threading scenarios −

using System;
using System.Threading;
using System.Threading.Tasks;

class ProducerConsumer {
    static object _lock = new object();
    static bool dataReady = false;
    
    public static void Main() {
        Task producer = Task.Run(() => Producer());
        Task consumer = Task.Run(() => Consumer());
        
        Task.WaitAll(producer, consumer);
    }
    
    static void Producer() {
        Monitor.Enter(_lock);
        try {
            Console.WriteLine("Producer: Creating data...");
            Thread.Sleep(2000);
            dataReady = true;
            Console.WriteLine("Producer: Data ready, notifying consumer");
            Monitor.Pulse(_lock);
        } finally {
            Monitor.Exit(_lock);
        }
    }
    
    static void Consumer() {
        Monitor.Enter(_lock);
        try {
            while (!dataReady) {
                Console.WriteLine("Consumer: Waiting for data...");
                Monitor.Wait(_lock);
            }
            Console.WriteLine("Consumer: Processing data");
        } finally {
            Monitor.Exit(_lock);
        }
    }
}

The output of the above code is −

Consumer: Waiting for data...
Producer: Creating data...
Producer: Data ready, notifying consumer
Consumer: Processing data

Comparison

Feature lock Statement Monitor Class
Syntax Simple, concise Verbose, explicit
Exception Safety Automatic try-finally Manual try-finally required
Timeout Support No Yes (TryEnter)
Wait/Pulse No Yes
Use Cases Basic synchronization Advanced threading scenarios

Conclusion

Use lock for simple thread synchronization as it provides cleaner syntax and automatic exception handling. Choose Monitor when you need advanced features like timeouts, conditional waiting with Wait() and Pulse(), or more granular control over the synchronization mechanism.

Updated on: 2026-03-17T07:04:36+05:30

2K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements