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 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(); } } }