C# Tutorial

C# String

C# Array

C# Flow Control

C# Class and Object

C# Inheritance

C# Interface

C# Collection

C# Generic

C# File I/O

C# Delegate and Event

C# Exception

C# Process and Thread

C# ADO.NET Database Operations

C# Monitor: Locked Resources

In this tutorial, we will discuss how to use the Monitor class in C# to lock resources and ensure thread synchronization in multi-threaded applications. The Monitor class provides a mechanism to ensure that only one thread can access a shared resource at a time, preventing race conditions and improving the reliability of concurrent code.

  • Monitor Basics

The Monitor class is part of the System.Threading namespace and provides methods to acquire and release exclusive locks on objects. The most commonly used methods are Enter, Exit, and TryEnter.

  • Enter: Acquires an exclusive lock on the specified object.
  • Exit: Releases an exclusive lock on the specified object.
  • TryEnter: Attempts to acquire an exclusive lock on the specified object, returning a boolean value that indicates whether the lock was acquired.
  • Using Monitor.Enter and Monitor.Exit

To use the Monitor.Enter and Monitor.Exit methods, you need to define a shared object that will be used as the synchronization lock. When a thread acquires the lock on this object, other threads attempting to acquire the same lock will be blocked until the lock is released.

Here's a simple example:

public class SharedResource
{
    private readonly object _lock = new object();
    private int _counter = 0;

    public void Increment()
    {
        Monitor.Enter(_lock);
        try
        {
            _counter++;
            Console.WriteLine($"Counter: {_counter}");
        }
        finally
        {
            Monitor.Exit(_lock);
        }
    }
}

In this example, the Increment method uses Monitor.Enter to acquire an exclusive lock on the _lock object, ensuring that only one thread can access the _counter at a time. The finally block ensures that the lock is released using Monitor.Exit even if an exception occurs within the try block.

  • Using Monitor.TryEnter

The Monitor.TryEnter method can be used to attempt to acquire an exclusive lock on an object without blocking if the lock is not immediately available.

Here's an example:

public void IncrementWithTimeout()
{
    if (Monitor.TryEnter(_lock, TimeSpan.FromSeconds(1)))
    {
        try
        {
            _counter++;
            Console.WriteLine($"Counter: {_counter}");
        }
        finally
        {
            Monitor.Exit(_lock);
        }
    }
    else
    {
        Console.WriteLine("Failed to acquire lock");
    }
}

In this example, the IncrementWithTimeout method uses Monitor.TryEnter to attempt to acquire the lock on the _lock object. If the lock is not available within one second, the method will not block and will simply output "Failed to acquire lock".

  • Example Usage

Here's an example of using the SharedResource class in a multi-threaded application:

public static void Main(string[] args)
{
    SharedResource sharedResource = new SharedResource();
    var threads = new List<Thread>();

    for (int i = 0; i < 5; i++)
    {
        Thread t = new Thread(() =>
        {
            for (int j = 0; j < 10; j++)
            {
                sharedResource.Increment();
                Thread.Sleep(50);
            }
        });
        threads.Add(t);
        t.Start();
    }

    foreach (var t in threads)
    {
        t.Join();
    }

    Console.WriteLine("All threads completed");
}

In this example, we create five threads that each call the Increment method on the sharedResource object. The Monitor class ensures that only one thread can access the _counter at a time.

  1. How to use Monitor in C#

    The Monitor class in C# is used for managing synchronization and preventing race conditions in multithreaded applications.

    using System;
    using System.Threading;
    
    class SharedResource
    {
        private object lockObject = new object();
    
        public void AccessResource()
        {
            // Using Monitor.Enter and Monitor.Exit to create a critical section
            Monitor.Enter(lockObject);
            try
            {
                // Code that requires exclusive access to the shared resource
                Console.WriteLine("Accessing the shared resource.");
            }
            finally
            {
                Monitor.Exit(lockObject);
            }
        }
    }
    
  2. Managing locked resources with Monitor in C#

    Monitor helps manage access to shared resources by creating critical sections where only one thread can execute at a time.

  3. C# Monitor.Enter and Monitor.Exit

    Monitor.Enter is used to acquire a lock on an object, and Monitor.Exit is used to release the lock.

    Monitor.Enter(lockObject);
    try
    {
        // Critical section
    }
    finally
    {
        Monitor.Exit(lockObject);
    }
    
  4. Thread synchronization using Monitor in C#

    Monitor ensures that only one thread at a time can access a critical section of code, preventing data corruption.

  5. Monitor.Wait and Monitor.Pulse in C#

    Used for thread coordination. Monitor.Wait releases the lock and waits for a signal (Monitor.Pulse) before continuing.

    Monitor.Enter(lockObject);
    try
    {
        // Code before waiting
        Monitor.Wait(lockObject); // Releases the lock and waits for Pulse
    
        // Code after being signaled
    }
    finally
    {
        Monitor.Exit(lockObject);
    }
    
  6. Deadlock prevention with Monitor in C#

    Careful use of Monitor can help prevent deadlocks, ensuring that threads release locks in a consistent order.

  7. C# Monitor class vs. lock statement

    lock is syntactic sugar for Monitor.Enter and Monitor.Exit. The lock statement simplifies the code for creating a critical section.

    // Using lock statement
    lock (lockObject)
    {
        // Critical section
    }
    
  8. Monitor class and multithreading in C#

    The Monitor class is essential for managing multithreading scenarios, providing synchronization and coordination mechanisms.

  9. Timeouts with Monitor in C#

    Monitor.TryEnter allows specifying a timeout for acquiring a lock, preventing potential deadlocks.

    if (Monitor.TryEnter(lockObject, TimeSpan.FromSeconds(1)))
    {
        try
        {
            // Code within the critical section
        }
        finally
        {
            Monitor.Exit(lockObject);
        }
    }
    
  10. C# ReaderWriterLockSlim vs. Monitor

    ReaderWriterLockSlim is an alternative to Monitor for managing access to shared resources with a focus on read and write operations.

  11. C# Monitor class and race conditions

    Proper use of Monitor helps prevent race conditions, ensuring that only one thread modifies shared resources at a time.

  12. Monitor class in asynchronous programming in C#

    Monitor can be used to synchronize access to shared resources in asynchronous programming scenarios.