1、AutoResetEvent 和 ManualResetEvent
ManualResetEvent
表示线程同步事件,收到信号时,必须手动重置该事件。AutoResetEvent
表示线程同步事件在一个等待线程释放后收到信号时自动重置。ManualResetEvent
调用一次Set()后将允许恢复所有被阻塞线程。需手动在调用WaitOne()
之后调用Reset()
重置信号量状态为非终止,然后再次调用WaitOne()
时才能继续阻塞线程。AutoResetEvent
调用一次Set()
只能继续被阻塞的一个线程,多次调用Set()
才行,但不需手动调用Reset()
;再次调用WaitOne()
的时候又能阻塞线程。AutoResetEvent
不保证每次调用 Set()
方法都将释放一个线程。 如果两次调用都过于接近,因此在释放线程之前发生第二次调用,则只释放一个线程。 这就像第二次调用没有发生。 此外,如果在没有 Set()
等待的线程并且已 AutoResetEvent
发出信号的情况下调用,则调用不起作用。
2、AutoResetEvent的使用
参考文档:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.autoresetevent?view=net-6.0
AutoResetEvent(false)
为创建在无信号状态。WaitOne()
方法会阻塞,调用Set()
方法变为有信号状态,只能继续被阻塞的一个线程的WaitOne()
方法,会自动变为信号状态,不需手动调用Reset()
。new AutoResetEvent(true)
为创建在有信号状态。调用Set()
方法变为有信号状态,只能继续被阻塞的一个线程的WaitOne()
方法,会自动变为信号状态,不需手动调用Reset()
。
例如,
using System;
using System.Threading;
namespace ConsoleApplication
{
class Program
{
private static AutoResetEvent event_1 = new AutoResetEvent(true);
private static AutoResetEvent event_2 = new AutoResetEvent(false);
static void Main()
{
for (int i = 1; i < 4; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
}
Thread.Sleep(250);
for (int i = 0; i < 2; i++)
{
Console.WriteLine("按Enter键释放另一个线程。");
Console.ReadLine();
event_1.Set();
Thread.Sleep(250);
}
Console.WriteLine("\r\nevent_2所有线程现在都在等待");
for (int i = 0; i < 3; i++)
{
Console.WriteLine("按Enter键释放线程。");
Console.ReadLine();
event_2.Set();
Thread.Sleep(250);
}
}
static void ThreadProc()
{
string name = Thread.CurrentThread.Name;
Console.WriteLine("{0} 等待event_1", name);
event_1.WaitOne();
Console.WriteLine("{0} 从event_1释放", name);
Console.WriteLine("{0} 等待event_2", name);
event_2.WaitOne();
Console.WriteLine("{0} 从event_2释放", name);
Console.WriteLine("{0} 结束", name);
}
}
}
3、ManualResetEvent的使用
参考文档:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.manualresetevent?view=net-6.0
new ManualResetEvent(false)
为创建在无信号状态。WaitOne()
方法会阻塞,调用Set()
方法变为有信号状态,WaitOne()
方法不会阻塞,需要调用Reset()
方法变为无信号状态,WaitOne()
方法将会阻塞。new ManualResetEvent(true)
为创建在有信号状态。Wait()
方法不会阻塞,需要调用Reset()
方法变为无信号状态,Wait()
方法将会阻塞。
例如,
using System;
using System.Threading;
namespace ConsoleApplication
{
class Program
{
//// mre用于手动阻塞和释放线程。false是创建在无信号状态。
private static ManualResetEvent mre = new ManualResetEvent(false);
static void Main()
{
Console.WriteLine("启动 3个在ManualResetEvent上阻塞的命名线程");
for (int i = 0; i <= 2; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
}
Thread.Sleep(500);
Console.WriteLine("按回车键,当三个线程都启动后,调用Set()释放所有线程。");
Console.ReadLine();
mre.Set();
Thread.Sleep(500);
Console.WriteLine("按回车键,当ManualResetEvent有信号时,调用WaitOne()的线程不会阻塞。");
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("按回车键,调用Reset(),这样线程在调用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("按回车键,调用Set()并结束运行。");
Console.ReadLine();
mre.Set();
// 如在Visual Studio中运行这个例子,取消下面这行注释:
//Console.ReadLine();
}
private static void ThreadProc()
{
string name = Thread.CurrentThread.Name;
Console.WriteLine(name + " 调用 mre.WaitOne()");
mre.WaitOne();
Console.WriteLine(name + " 结束");
}
}
}