在.Net多线程编程中,AutoResetEvent和ManualResetEvent这两个类经常用到,用于线程之间的通信, 他们的用法很类似,但也有区别。Set方法将信号置为发送状态,Reset方法将信号置为不发送状态;WaitOne方法在无信号状态下,可以使当前线程挂起;注意这里说的是当前线程。可以通过构造函数的参数值来决定其初始状态,true表示畅通无阻的状态(signaled),线程不能阻塞,即使调用WaitOne也不能阻塞;false表示是可阻塞的状态(nonsignaled),可以调用WaitOne方法来阻塞,WaitOne方法被调用后,当前线程(即调用这个方法的线程)暂停运行(可以看做等待事件触发),直到事件被触发(即另外的线程调用了它的Set方法)。WaitOne阻塞当前线程直到别的线程调用Set方法,但只有ManualResetEvent的状态是可阻塞(nonsignaled)时,WaitOne才能真正的起到作用。不管之前是什么状态,调用Reset都把状态设置为阻塞的(nonsignaled)。其区别就在调用后,AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号后,AutoResetEvent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只有继续等待.也就是说,AutoResetEvent一次只唤醒一个线程;而ManualResetEvent则可以唤醒多个线程,因为当某个线程调用了ManualResetEvent.Set()方法后,其他调用WaitOne的线程获得信号得以继续执行,而ManualResetEvent不会自动将信号置为不发送。也就是说,除非手工调用了ManualResetEvent.Reset()方法,则ManualResetEvent将一直保持有信号状态,ManualResetEvent也就可以同时唤醒多个线程继续执行。
在多线程的代码里,可以使用一个ManualResetEvent对象来控制线程所有线程;只要在调用WaitOne()方法前,调用Reset()方法,因为WaitOne()控制的是当前线程;但是这样做,ManualResetEvent对象的管理逻辑会变得复杂;所以这里建议一条线程一个ManualResetEvent对象。
namespace ResetEventTest
{
class Program
{
private static ManualResetEvent evt = new ManualResetEvent(true);
static void Main(string[] args)
{
Boy sender = new Boy(evt);
Thread th = new Thread(new ThreadStart(sender.SendFlower));
th.Start();
Console.WriteLine("送花途中,我等待");
evt.Reset();
evt.WaitOne(); //等待工作,阻塞线程
Console.WriteLine("收到了吧,是我送的");
Console.ReadLine();
}
}
public class Boy
{
ManualResetEvent evt;
public Boy(ManualResetEvent _evt)
{
evt = _evt;
}
public void SendFlower()
{
Console.WriteLine("正在送花途中");
for (int i = 0; i < 10; i++)
{
Thread.Sleep(500);
Console.Write(".");
}
Console.WriteLine("\r\n花已经送到MM手中^_^");
evt.Set(); //通知阻塞程序
}
}
}
========================================================
namespace TestMoreResetEvent
{
class Program
{
static void Main(string[] args)
{
EventWaitTest zhangsan = new EventWaitTest("张三");
EventWaitTest lisi = new EventWaitTest("李四");
Thread t1 = new Thread(new ThreadStart(zhangsan.Consume));
Thread t2 = new Thread(new ThreadStart(lisi.Consume));
Thread t3 = new Thread(new ThreadStart(EventWaitTest.Product));
t1.Start();
t2.Start();
t3.Start();
Console.Read();
}
}
public class EventWaitTest
{
private string name; //顾客姓名
//private static AutoResetEvent eventWait = new AutoResetEvent(false);
private static ManualResetEvent eventWait = new ManualResetEvent(false);
private static ManualResetEvent eventOver = new ManualResetEvent(false);
public EventWaitTest(string name)
{
this.name = name;
}
public static void Product()
{
Console.WriteLine("服务员:厨师在做菜呢,两位稍等");
Thread.Sleep(2000);
Console.WriteLine("服务员:宫爆鸡丁好了");
eventWait.Set();
while (true)
{
if (eventOver.WaitOne(1000, false))
{
Console.WriteLine("服务员:两位请买单");
eventOver.Reset();
}
}
}
public void Consume()
{
while (true)
{
if (eventWait.WaitOne(1000, false))
{
Console.WriteLine(this.name + ":开始吃宫爆鸡丁");
Thread.Sleep(2000);
Console.WriteLine(this.name + ":宫爆鸡丁吃光了");
eventWait.Reset();
eventOver.Set();
break;
}
else
{
Console.WriteLine(this.name + ":等着上菜无聊先玩会手机游戏");
}
}
}
}
}
===============================================================
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static ManualResetEvent auto = new ManualResetEvent(false);
static void Main(string[] args)
{
Thread t = new Thread(WaitFoSingalToWrite);
t.Start();
Thread t1 = new Thread(WaitFoSingalToPlay);
t1.Start();
Thread.Sleep(2000);
auto.Set();
Console.WriteLine("Main End...");
Console.ReadLine();
}
static void WaitFoSingalToWrite()
{
Console.WriteLine("do in...");
auto.WaitOne();
Console.WriteLine("do sth..." + DateTime.Now);
auto.WaitOne(3000, true);
Console.WriteLine("do sth 1..." + DateTime.Now);
Console.WriteLine("do out...");
}
static void WaitFoSingalToPlay()
{
Console.WriteLine("Play in...");
auto.WaitOne();
Console.WriteLine("Play sth..." + DateTime.Now);
auto.WaitOne(3000, true);
Console.WriteLine("Play sth 1..." + DateTime.Now);
Console.WriteLine("Play out...");
}
}
}