在 .Net 多线程中可以使用 ManualResetEvent 和 AutoResetEvent 来协调不同的线程的运行。文档中说这两个类都可以通过 set 方法释放信号,等待信号的线程可以通过捕获信号来控制线程的运行,和 ManualResetEvent 不同的是 AutoResetEvent 的 set 方法被调用以后,它会自动 reset。那么这个自动 reset 有什么用处呢?
简单的说 AutoResetEvent 的 set 和 reset 是一个原子操作,如果有多个线程等待 AutoResetEvent 的信号,那么只会有一个线程捕捉到信号。同样的情况下,ManualResetEvent 在没有 reset 之前所有的线程都可以捕捉到信号。
我们来看看下面的 c# 代码:
public class AutoResetEventSample
{
private AutoResetEvent autoReset = new AutoResetEvent(false);
public void RunAll()
{
new Thread(Worker1).Start();
new Thread(Worker2).Start();
new Thread(Worker3).Start();
autoReset.Set();
Thread.Sleep(1000);
autoReset.Set();
Console.WriteLine("主线程抵达最后一行。");
}
public void Worker1()
{
Console.WriteLine("进入线程 1");
for (int i = 0; i < 5; i++)
{
autoReset.WaitOne();
Console.WriteLine("线程 1 抓到信号");
}
}
public void Worker2()
{
Console.WriteLine("进入线程 2");
for (int i = 0; i < 5; i++)
{
autoReset.WaitOne();
Console.WriteLine("线程 2 抓到信号");
}
}
public void Worker3()
{
Console.WriteLine("进入线程3");
for (int i = 0; i < 5; i++)
{
autoReset.WaitOne();
Console.WriteLine("线程 3 抓到信号");
}
}
}
我们会看到类似于下面的运行结果。每一次触发只会有一个线程捕捉到信号!
再看下面的代码,同样的多线程等待,我们使用 ManualResetEvent 来触发,因为没有 reset 所有线程都收到了信号。
public class ManualResetEventSample
{
private ManualResetEvent manualReset = new ManualResetEvent(false);
public void RunAll()
{
new Thread(Worker1).Start();
new Thread(Worker2).Start();
new Thread(Worker3).Start();
Thread.Sleep(1000);
manualReset.Set();
Console.WriteLine("主线程抵达最后一行。");
}
public void Worker1()
{
Console.WriteLine("进入线程 1");
//for (int i = 0; i < 5; i++)
{
manualReset.WaitOne();
Console.WriteLine("线程 1 抓到信号");
}
}
public void Worker2()
{
Console.WriteLine("进入线程 2");
//for (int i = 0; i < 5; i++)
{
manualReset.WaitOne();
Console.WriteLine("线程 2 抓到信号");
}
}
public void Worker3()
{
Console.WriteLine("进入线程3");
//for (int i = 0; i < 5; i++)
{
manualReset.WaitOne();
Console.WriteLine("线程 3 抓到信号");
}
}
}
运行结果:
结论
ManualResetEvent 和 AutoResetEvent 的区别是,前者好比一扇普通的门,当一个人打开了以后,如果不主动的关上,那么后面的人都可以进入,而后者好比检票机的闸门是自动门,这种自动门每次打开进一个人以后就会被关上。