The use of ManualResetEvent and ManualResetEventSlim in C#

Starting with .NET Framework version 2.0, ManualResetEvent derives from the EventWaitHandle class. The ManualResetEvent is functionally equivalent to the EventWaitHandle created using EventResetMode.ManualReset. ManualResetEventSlim is used to achieve better performance of ManualResetEvent. The following introduces a summary of the use of ManualResetEvent and ManualResetEventSlim in .NET (C#).

1. ManualResetEvent and ManualResetEventSlim

ManualResetEvent represents a thread synchronization event. When a signal is received, the event must be manually reset. This class cannot be inherited. ManualResetEventSlim is used to achieve better performance than ManualResetEvent , where instead the wait time is expected to be very short and the event does not cross process boundaries. ManualResetEventSlim briefly uses busy spin while waiting for the event to be signaled. When the waiting time is short, the overhead of rotation will be much less than the overhead of waiting using a wait handle. However, if the event is not signaled within a certain period of time, ManualResetEventSlim will resort to regular event handler waiting. Starting with .NET Framework version 4.0, the System.Threading.ManualResetEventSlim class is a lightweight replacement for ManualResetEvent .

2. Use of ManualResetEvent

Reference documentation: ManualResetEvent class (System.Threading) | Microsoft Learn

new ManualResetEvent(false) is created in a no-signal state. The WaitOne() method will block. Call the Set() method to set it to a signal state. The WaitOne() method will not block and you need to call Reset. () method becomes unsignaled, and the WaitOne() method will block. new ManualResetEvent(true) is created in a signal state. The WaitOne() method will not block. You need to call the Reset() method to become a no-signal state. The WaitOne() method will block.

For example,

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
    class Program
    {
         mre is used to manually block and release threads. false is created in a no-signal state.
        private static ManualResetEvent mre = new ManualResetEvent(false);
        static void Main()
        {
            Console.WriteLine("Start 3 named threads blocked on ManualResetEvent");
            for (int i = 0; i <= 2; i + + )
            {
                Thread t = new Thread(ThreadProc);
                t.Name = "Thread_" + i;
                t.Start();
            }
            Thread.Sleep(500);
            Console.WriteLine("Press the Enter key. After all three threads are started, call Set() to release all threads.");
            Console.ReadLine();
            mre.Set();
            Thread.Sleep(500);
            Console.WriteLine("Press the Enter key. When ManualResetEvent has a signal, the thread calling WaitOne() will not block.");
            Console.ReadLine();
            for (int i = 3; i <= 4; i + + )
            {
                Thread t = new Thread(ThreadProc);
                t.Name = "Thread_" + i;
                t.Start();
            }
            Thread.Sleep(500);
            Console.WriteLine("Press the Enter key and call Reset(), so that the thread will block again when calling WaitOne().");
            Console.ReadLine();
            mre.Reset();
            // Start a thread that waits on the ManualResetEvent.
            Thread t5 = new Thread(ThreadProc);
            t5.Name = "Thread_5";
            t5.Start();
            Thread.Sleep(500);
            Console.WriteLine("Press the Enter key to call Set() and end the run.");
            Console.ReadLine();
            mre.Set();
            // To run this example in Visual Studio, uncomment the following line:
            //Console.ReadLine();
        }
        private static void ThreadProc()
        {
            string name = Thread.CurrentThread.Name;
            Console.WriteLine(name + "Call mre.WaitOne()");
            mre.WaitOne();
            Console.WriteLine(name + "end");
        }
    }
}

3. Use of ManualResetEventSlim

Reference documentation: ManualResetEventSlim class (System.Threading) | Microsoft Learn

new ManualResetEventSlim(false) is created in a no-signal state. The Wait() method will block. Call the Set() method to set it to a signal state. The Wait() method will not block. You need to call Reset() method becomes unsignaled, and the Wait() method will block. new ManualResetEventSlim(true) is created in a signal state. The Wait() method will not block. You need to call the Reset() method to become a no-signal state. The Wait() method will block.

For example,

using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
    class Program
    {
        static void Main()
        {
            MRES_SetWaitReset();
            MRES_SpinCountWaitHandle();
        }
        //
        // ManualResetEventSlim construction
        // ManualResetEventSlim.Wait()
        // ManualResetEventSlim.Set()
        // ManualResetEventSlim.Reset()
        // ManualResetEventSlim.IsSet
        static void MRES_SetWaitReset()
        {
            ManualResetEventSlim mres1 = new ManualResetEventSlim(false); //Initially created in no signal state
            ManualResetEventSlim mres2 = new ManualResetEventSlim(false); //Initially created in no signal state
            ManualResetEventSlim mres3 = new ManualResetEventSlim(true); // Initial creation is in signal state
                                                                          // Start an asynchronous task to operate mres3 and mres2
            var observer = Task.Factory.StartNew(() =>
            {
                mres1.Wait();
                Console.WriteLine("observer mres1 signal");
                Console.WriteLine("observer Reset mres3 signal");
                mres3.Reset(); // mres3 should have no signal
                Console.WriteLine("observer Set mres2");
                mres2.Set();
            });
 
            Console.WriteLine("Main thread: mres3.IsSet = {0} (should be true)", mres3.IsSet);
            Console.WriteLine("Main Thread Set mres1");
            mres1.Set(); // observer Task will be executed
            mres2.Wait(); // observer Task completes Reset mres3, Set mres2
            Console.WriteLine("Main thread mres2 has signal");
            Console.WriteLine("Main thread: mres3.IsSet = {0} (should be false)", mres3.IsSet);
 
            //When you finish using ManualResetEventSlim, Dispose() a ManualResetEventSlim to release resources
            observer.Wait(); // Ensure that observer execution is completed
            mres1.Dispose();
            mres2.Dispose();
            mres3.Dispose();
            // To run this example in Visual Studio, uncomment the following line:
            Console.ReadLine();
        }
 
        // :
        // ManualResetEventSlim construction w/ SpinCount
        // ManualResetEventSlim.WaitHandle
        static void MRES_SpinCountWaitHandle()
        {
            // Construct a ManualResetEventSlim with a SpinCount of 1000
            ManualResetEventSlim mres1 = new ManualResetEventSlim(false, 1000);
            ManualResetEventSlim mres2 = new ManualResetEventSlim(false, 1000);
 
            Task bgTask = Task.Factory.StartNew(() =>
            {
                // Just wait a little
                Thread.Sleep(100);
 
                // Signal two MRESes
                Console.WriteLine("mres1.Set() mres2.Set()");
                mres1.Set();
                mres2.Set();
            });
            //Wait for all elements in the specified array to receive the signal.
            WaitHandle.WaitAll(new WaitHandle[] { mres1.WaitHandle, mres2.WaitHandle });
            Console.WriteLine("WaitHandle.WaitAll(mres1.WaitHandle, mres2.WaitHandle) completed.");
 
            //Clean up
            bgTask.Wait();
            mres1.Dispose();
            mres2.Dispose();
        }
    }
}