To recap, in the earlier sections on lock and Monitor, we learned about thread locks, in Mutex we learned about process synchronization, and in Semaphore we studied resource pool limits.
In this article, we will learn about the AutoRestEvent class used in C# for sending thread notifications.
AutoRestEvent Class
Used to send notifications from one thread to another.
The Microsoft documentation describes it as: Represents a thread synchronization event that automatically resets after a waiting thread receives a signal.
It has only one constructor:
The parameter in the constructor is used to set the signal state.
| Constructor | Description |
|--------------|-------------|
| AutoResetEvent(Boolean) | Initializes a new instance of the AutoResetEvent class with a Boolean value indicating whether to set the initial state to signaled. |
Terrible machine translation.
Common Methods
What does the AutoResetEvent class do and what does the constructor's parameter do? No need to rush, let’s first look at the commonly used methods of this class:
| Method | Description |
|--------|-------------|
| Close() | Releases all resources used by the current WaitHandle. |
| Reset() | Sets the event state to non-signaled, causing threads to block. |
| Set() | Sets the event state to signaled, allowing one or more waiting threads to proceed. |
| WaitOne() | Blocks the current thread until the current WaitHandle receives a signal. |
| WaitOne(Int32) | Blocks the current thread until the current WaitHandle receives a signal or a time interval (in milliseconds) specified by a 32-bit signed integer elapses. |
| WaitOne(Int32, Boolean) | Blocks the current thread until the current WaitHandle receives a signal or a time interval specified by a 32-bit signed integer elapses, and specifies whether to exit the synchronization domain before waiting. |
| WaitOne(TimeSpan) | Blocks the current thread until the current instance receives a signal, while specifying a time interval as a TimeSpan. |
| WaitOne(TimeSpan, Boolean) | Blocks the current thread until the current instance receives a signal, specifying a time interval as a TimeSpan, and whether to exit the synchronization domain before waiting. |
A Simple Example
Let’s write a program like this:
Create a thread that can perform tasks in multiple stages; after completing each stage, it needs to pause and wait for a notification from a child thread before proceeding to the next execution step.
.WaitOne()
is used to wait for another thread to send a notification;
.Set()
is used to notify the thread, putting AutoResetEvent
into a signaled state;
.Reset()
is used to reset the AutoResetEvent
state;
class Program
{
// Thread notification
private static AutoResetEvent resetEvent = new AutoResetEvent(false);
static void Main(string[] args)
{
// Create a thread
new Thread(DoOne).Start();
// Continuously send signals to another thread
while (true)
{
Console.ReadKey();
resetEvent.Set(); // Send notification, set signaled state
}
}
public static void DoOne()
{
Console.WriteLine("Waiting, please send a signal to allow me to run");
// Wait for other threads to send a signal
resetEvent.WaitOne();
Console.WriteLine("\n Signal received, continuing execution");
for (int i = 0; i
Explanation
The AutoResetEvent object has signaled and non-signaled states. Set()
sets the signaled state, while Reset()
resets to the non-signaled state.
The signaled state can be understood as a signal that has been notified; the non-signaled state indicates that the signal has not been notified.
Note that the signaled and non-signaled states refer to the state of the AutoResetEvent, not the state of the thread.
It is noteworthy that if the AutoResetEvent is already in the signaled state, calling WaitOne()
on a thread will not have any effect unless Reset()
is called.
The parameter in the constructor is precisely for setting this state. true
represents the signaled state while false
represents the non-signaled state. If you use new AutoResetEvent(true);
, the thread will not need to wait for a signal initially.
After using the type, you should release it explicitly by calling Close()/Dispose()
or using using
. Of course, you can also simply exit the program.
It is worth mentioning that if Set()
is called multiple times with very short intervals, the second Set()
may become ineffective (not work) if the first Set()
has yet to be processed (sending a signal takes time).
A More Complex Example
Let’s design a program:
- Two threads start in a blocked state;
- Thread One can set Thread Two to continue running and then block itself;
- Thread Two can set One to continue running and then block itself;
The program code is as follows (after running, please set the keyboard to English input state and then press a key):
class Program
{
// Control the first thread
// The first thread starts with the AutoResetEvent in a signaled state, no need to wait for a signal
private static AutoResetEvent oneResetEvent = new AutoResetEvent(true);
// Control the second thread
// The second thread starts with the AutoResetEvent in a non-signaled state, needs to wait for a signal
private static AutoResetEvent twoResetEvent = new AutoResetEvent(false);
static void Main(string[] args)
{
new Thread(DoOne).Start();
new Thread(DoTwo).Start();
Console.ReadKey();
}
public static void DoOne()
{
while (true)
{
Console.WriteLine("\n① Press a key, and I will let DoTwo run");
Console.ReadKey();
twoResetEvent.Set();
oneResetEvent.Reset();
// Wait for DoTwo() to send me a signal
oneResetEvent.WaitOne();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("\n DoOne() is executing");
Console.ForegroundColor = ConsoleColor.White;
}
}
public static void DoTwo()
{
while (true)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
// Wait for DoOne() to send me a signal
twoResetEvent.WaitOne();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("\n DoTwo() is executing");
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("\n② Press a key, and I will let DoOne run");
Console.ReadKey();
oneResetEvent.Set();
twoResetEvent.Reset();
}
}
}
Explanation
The two threads have the functionality of blocking themselves and releasing the other thread from blocking.
To understand this, we can reference a scene from the movie "The Best Partnership."
DoOne and DoTwo alternate breathing; they cannot control their own breathing but can determine when the other can breathe.
If you help me, I can help you, and then we can continue breathing.
Of course, WaitOne()
can also set a waiting time. If the bald guy (DoOne) is being difficult and doesn’t let King Kong (DoTwo) breathe, King Kong can forcefully adjust the balance and breathe after waiting for a certain amount of time.
AutoResetEvent is also suitable for thread synchronization.
Additionally, when one thread uses WaitOne()
and the other thread uses Set()
to send a notification, the AutoResetEvent object will automatically revert to a non-signaled state; there's no need for the thread to use Reset()
.
文章评论