多线程---使用ManualResetEvent来控制线程间的同步(实现了消费者和生产者模式)...

ManualResetEvent允许线程间通过发信号来相互通信,一般用于一个线程完成某件事情之后通知另外一个线程继续运行。

其构造函数,需要bool性参数,true表示有信号,false表示没有信号。

主要方法介绍:

  • Set,发信号,说明当前线程已经完成,等待线程检测到信号后就可以执行了。
  • ReSet,将信号置为无效。
  • WaitOne,在没有接收到信号之前,阻止当前线程,让当前线程继续等待(注意很容易死掉的)

  在前几天转载的一片文章(多线程--C#利用多线程实现消费者和生产者模式),实现了消费者和生产者模式,主要是控制生产者和消费者的顺序,是通过Monitor来实现的。现在我更改为由ManualResetEvent实现,原理是比较简单的:

  • 生产者生产完产品后通过Set方法告知消费者消费
  • 消费者通过WaitOne(没有接到信号之前一直等待)来等待生产者Set(发型号)
  • 消费者消费完成之后通过ReSet将信号置为无效(以免对同一产品多次消费),继续等待生产者生产的产品(等待生产者Set)

  上代码(注意里面为数不多的注释

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
namespace Alex.MultiThread
{
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(); // 告诉自己,这个东西我已经消费了,
        //不能再次消费了,只能WaitOne下一次了
}
}
}

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;
}
}
}

这个程序是可以没有问题的执行的,截图如下:2010082620364136.jpg

(是从1开始的,没有全部截下来)

但是上面程序其实是有问题的。如果去掉Thread.Sleep(1000);这个代码会进入无限期的等待中,永远运行不下去。为什么?

  因为没有Thread.Sleep(1000);生产者可能事先把所有的东西生产完之后,程序才开始执行消费者方法。而消费者方法

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
public void ThreadRun()
{
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类似。明天继续总结一下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值