本文不打算详细介绍这两个东东,只是简单给个示例说明下两者的区别。
背 景
在多线程的环境下,我们有时候希望某个线程在其它线程之后再继续执行,比如在操作一个文本的时候,一个线程执行删除,另一个线程执行写入,这时候就需要通过某种方法让这两个线程按照次序执行。这也就是所谓的线程同步,这个 “同” 字应该理解为协同、合作。而信号量就是其中一种线程同步的方式,就好像红绿灯一样,当信号为红灯时,我们知道要停止,变成绿灯时,就可以通行了。所有采用信号量的类都是基于 WaitHandle 这个类的实现。
我们的主角 ManualResetEvent 和 AutoResetEvent 继承自 EventWaitHandle, 它正是实现了 WaitHandler。EventWaitHandle 提供了 Set(释放信号量)、Reset(终止信号量)方法用于进行线程同步。下面,我就提供两个简单的示例来演示一下如何操作。
WaitHandle 实际使用的是 Windows 的内核对象,当我们使用该对象的时候需要从用户模式切换到内核模式,这会非常消耗性能。参考谈谈用内核对象进行线程同步
区别演示
在进入正题前,先来看一段简单的动画演示,快速了解下这两者的区别。
ManualResetEvent 效果
AutoResetEvent 效果
ManualResetEvent
Manual 的中文意思是 “手动”,也就是这是一个需要手动终止的事件,换句话说,如果不手动终止,那么信号一直为释放状态,相当于一直是绿灯。
调用 Set 表示释放信号,之后只有当调用了 Reset 方法后,信号才能重新成为终止状态。
private ManualResetEvent mre = new ManualResetEvent(false); //初始时让信号为终止状态 public void Suspend() { mre.Reset(); } public void Resume() { mre.Set(); } public void CreateThreadsAndRun() { Thread t = new Thread(Run); t.Start(); } private void Run(object obj) { while (true) { mre.WaitOne(); //如果信号为终止状态,那么这句话将会阻塞当前线程,直到收到信号释放的状态 mre.WaitOne(); //当信号为释放状态时,所有 WaitOne 都不会阻塞。 Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " is running..."); Thread.Sleep(2000); } }
AutoResetEvent
Auto 的中文意思是 “自动”,和 Manual 正好相反。所以大家应该已经猜到这两者的区别了,当信号为释放状态,只要有线程调用了一次 WaitOne ,那么信号就变成 “终止” 状态。
private AutoResetEvent are = new AutoResetEvent(true); //初始为 释放 状态 public void Suspend() { are.Reset(); } public void Resume() { are.Set(); } public void CreateThreadsAndRun() { Thread t = new Thread(Run); t.Start(); } private void Run(object obj) { while (true) { are.WaitOne(); Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " is running..."); //第一次因为是 释放 状态,该句被打印在屏幕上 are.WaitOne(); //因为已经调用过一次 WaitOne ,所以第二次调用 WaitOne 时,信号已经变成 终止 状态。该句将阻塞当前线程。 Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " is running..."); Thread.Sleep(2000); } }