C#关于AutoResetEvent的使用介绍
AutoResetEvent 允许线程通过发信号互相通信。 通常,当线程需要独占访问资源时使用该类。
线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号。 如果 AutoResetEvent 为非终止状态,则线程会被阻止,并等待当前控制资源的线程通过调用 Set 来通知资源可用。
调用 Set 向 AutoResetEvent 发信号以释放等待线程。 AutoResetEvent 将保持终止状态,直到一个正在等待的线程被释放,然后自动返回非终止状态。 如果没有任何线程在等待,则状态将无限期地保持为终止状态。
如果当 AutoResetEvent 为终止状态时线程调用 WaitOne,则线程不会被阻止。 AutoResetEvent 将立即释放线程并返回到未触发状态[1]。
MSDN中如是说。但听起来很玄乎,通俗地讲,当多个线程访问共享资源时,为保证线程执行的先后顺序,就要用到该类。举例来说,A、B、C三个线程,共同访问一个变量V,但必须保证执行顺序为A-》B-》C。可以设置两个resetevent,当A执行完毕,则向B发出信号,B执行完毕,则向C发出信号。
下面是一个网上买书的例子[2]。网上买书的执行流程如下:(1)我先选好一本书,(2)然后付钱,(3)最后卖家发货。这个顺序不能颠倒(排除货到付款的情况)。假设每个步骤一个线程,程序可以这么设计:步骤(1)为主线程,步骤(2)、(3)是两个辅助线程。代码如下:
using System; using System.Linq; using System.Activities; using System.Activities.Statements; using System.Threading; namespace CaryAREDemo { class Me { const int numIterations = 550; //买书的本数 //付账信号控制 static AutoResetEvent PayMoneyEvent = new AutoResetEvent(false); //发货信号控制 static AutoResetEvent DeliveEvent = new AutoResetEvent(false); //static ManualResetEvent PayMoneyEvent = new ManualResetEvent(false); //static ManualResetEvent DeliveEvent= new ManualResetEvent(false); static int number; //这是关键资源 static void Main() { Thread payMoneyThread = new Thread(new ThreadStart(PayMoneyProc)); payMoneyThread.Name = "付账线程"; Thread getBookThread = new Thread(new ThreadStart(GetBookProc)); getBookThread.Name = "发货线程"; payMoneyThread.Start(); getBookThread.Start(); for (int i = 1; i <= numIterations; i++) { Console.WriteLine("选书线程:数量{0}", i); number = i; //Signal that a value has been written. PayMoneyEvent.Set(); Thread.Sleep(500); //确保另外两个线程执行完毕 } payMoneyThread.Abort(); getBookThread.Abort(); } static void PayMoneyProc() { while (true) { PayMoneyEvent.WaitOne(); //PayMoneyEvent.Reset(); Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number); DeliveEvent.Set(); } } static void GetBookProc() { while (true) { DeliveEvent.WaitOne(); //DeliveEvent.Reset(); Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number); Console.WriteLine("------------------------------------------"); } } } }
运行结果如下:
...
选书线程:数量455
付账线程:数量455
发货线程:数量455
------------------------------------------
选书线程:数量456
付账线程:数量456
发货线程:数量456
------------------------------------------
...
AutoResetEvent与ManualResetEvent的区别
他们的用法\声明都很类似,Set方法将信号置为发送状态 Reset方法将信号置为不发送状态WaitOne等待信号的发送。其实,从名字就可以看出一个手动,
一个自动,这个手动和自动实际指的是在Reset方法的处理上,如下面例子:
public AutoResetEvent autoevent=new AutoResetEvent(true);
public ManualResetEvent manualevent=new ManualResetEvent(true);
默认信号都处于发送状态,
autoevent.WaitOne();
manualevent.WaitOne();
如果某个线程调用上面该方法,则当信号处于发送状态时,该线程会得到信号,得以继续执行。差别就在调用后,autoevent.WaitOne()每次只允许一个线程进入,当某个线程得到信号(也就是有其他线程调用了autoevent.Set()方法后)后,autoevent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只有继续等待.也就是说,autoevent一次只唤醒一个线程。而manualevent则可以唤醒多个线程,因为当某个线程调用了set方法后,其他调用waitone的线程获得信号得以继续执行,而manualevent不会自动将信号置为不发送.也就是说,除非手工调用了manualevent.Reset().方法,则manualevent将一直保持有信号状态,manualevent也就可以同时唤醒多个线程继续执行。如果上面的程序换成ManualResetEvent的话,就需要在waitone后面做下reset。
[1] http://msdn.microsoft.com/zh-cn/library/system.threading.autoresetevent(v=vs.110).aspx
[2] http://www.cnblogs.com/lzjsky/archive/2011/07/11/2102794.html,例子、代码均有改动。
0 条评论