ManualResetEvent允许线程间通过发信号来相互通信,一般用于一个线程完成某件事情之后通知另外一个线程继续运行。
其构造函数,需要bool性参数,true表示有信号,false表示没有信号。
主要方法介绍:
- Set,发信号,说明当前线程已经完成,等待线程检测到信号后就可以执行了。
- ReSet,将信号置为无效。
- WaitOne,在没有接收到信号之前,阻止当前线程,让当前线程继续等待(注意很容易死掉的)
在前几天转载的一片文章(多线程--C#利用多线程实现消费者和生产者模式),实现了消费者和生产者模式,主要是控制生产者和消费者的顺序,是通过Monitor来实现的。现在我更改为由ManualResetEvent实现,原理是比较简单的:
- 生产者生产完产品后通过Set方法告知消费者消费
- 消费者通过WaitOne(没有接到信号之前一直等待)来等待生产者Set(发型号)
- 消费者消费完成之后通过ReSet将信号置为无效(以免对同一产品多次消费),继续等待生产者生产的产品(等待生产者Set)
上代码(注意里面为数不多的注释)
![ContractedBlock.gif](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
{
public class Cell
{
int cellContents;
public int ReadFromCell() // 读(消费)
{
lock ( this )
{
Console.WriteLine( " Consume:{0} " , cellContents);
}
return cellContents;
}
public void WriteToCell( int n) // 写(生产)
{
lock ( this )
{
cellContents = n;
Console.WriteLine( " Produce:{0} " , cellContents);
}
}
}
/// <summary>
/// 生产者
/// </summary>
public class CellProd
{
Cell cell;
int quantity = 1 ; // 生产者生产次数
ManualResetEvent eventX;
public CellProd(Cell cell, int request,ManualResetEvent eventX)
{
this .cell = cell;
quantity = request;
this .eventX = eventX;
}
public void ThreadRun()
{
for ( int looper = 1 ;looper <= quantity;looper ++ )
{
cell.WriteToCell(looper); // 生产
eventX.Set(); // 发信号,告诉消费者我已经生产,你可以消费啦
Thread.Sleep( 1000 );
}
}
}
/// <summary>
/// 消费者
/// </summary>
public class CellCons
{
Cell cell;
int quantity = 1 ;
ManualResetEvent eventX;
public CellCons(Cell cell, int request, ManualResetEvent eventX)
{
this .cell = cell;
quantity = request;
this .eventX = eventX;
}
public void ThreadRun()
{
for ( int looper = 1 ;looper <= quantity;looper ++ )
{
eventX.WaitOne(); // 一直在苦苦等待生产者生产的产品
cell.ReadFromCell(); // 等到了,消费
eventX.Reset(); // 告诉自己,这个东西我已经消费了,
}
}
}
public class MonitorSample
{
public static void Main()
{
int result = 0 ;
Cell cell = new Cell();
ManualResetEvent eventX = new ManualResetEvent( false );
CellProd prod = new CellProd(cell, 20 ,eventX);
CellCons cons = new CellCons(cell, 20 ,eventX);
Thread producer = new Thread( new ThreadStart(prod.ThreadRun));
Thread consumer = new Thread( new ThreadStart(cons.ThreadRun));
try
{
producer.Start();
consumer.Start();
producer.Join();
consumer.Join();
Console.ReadLine();
}
catch (ThreadStartException e)
{
Console.WriteLine(e);
result = 1 ;
}
catch (ThreadInterruptedException e)
{
Console.WriteLine(e);
result = 1 ;
}
Environment.ExitCode = result;
}
}
}
这个程序是可以没有问题的执行的,截图如下:
(是从1开始的,没有全部截下来)
但是上面程序其实是有问题的。如果去掉Thread.Sleep(1000);这个代码会进入无限期的等待中,永远运行不下去。为什么?
因为没有Thread.Sleep(1000);生产者可能事先把所有的东西生产完之后,程序才开始执行消费者方法。而消费者方法
![ContractedBlock.gif](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
{
for ( int looper = 1 ;looper <= quantity;looper ++ )
{
eventX.WaitOne();
cell.ReadFromCell(); // 消费
eventX.Reset();
}
}
首先是WaitOne,最后是Reset,而Reset将信号置为了false,此时生产者已经结束(不会再有Set方法,信号永远为False)。当消费者再此遇到WaitOne的时候将进入无限期的等待。
但是当有Thread.Sleep(1000)时,生产者生产完之后生产线程暂停,程序立马会转向消费者线程,这样就形成了生产者和消费者的互动,一个reset,一个set。消费者就不会无限期的WaitOne。
其实还有一个类似的东东AutoResetEvent,它的功能和ManualResetEvent类似。明天继续总结一下。