本文用经典的生产者消费者的例子,说明Mnitor对象的用法。实现的目标是:
生产者生产一个数据,然后消费者开始消费,消费完成后生产者继续生产,循环10次后结束。
下面是代码:
namespace ConsoleTest
{
/// <summary>
/// 生产者消费者例子
/// </summary>
public class Producer_Consumer
{
/// <summary>
/// 生产者生产数据
/// </summary>
private void produce()
{
for (int i = 0; i < 10;i++ )
{
try
{
Monitor.Enter(_locker); //在调用Wait方法前必须获取对象的锁
{
Monitor.Wait(_locker); //阻塞当前线程,释放锁,进入等待状态
Thread.Sleep(500); //此处是为了让程序的执行过程更清楚
_count++;
Console.WriteLine("生产者: " + _count);
Monitor.Pulse(_locker); //通知等待获取锁(即调用Wait方法的线程)的消费者线程进入就绪状态
}
}
finally
{
Monitor.Exit(_locker); //释放锁
}
}
}
/// <summary>
/// 消费者消费数据
/// </summary>
private void consume()
{
try
{
Monitor.Enter(_locker); //在调用Pulse方法前必须获取对象的锁
{
Monitor.Pulse(_locker); //此处不加这句代码会造成死锁
while (Monitor.Wait(_locker)) //循环等待生产者线程生产数据完成
{
Thread.Sleep(500);
Console.WriteLine("消费者:" + _count);
Monitor.Pulse(_locker); //消费完成后,通知生产者继续生产
if (_count == 10)
{
Console.ReadLine();
return; //消费完10个数据后程序退出
}
}
}
}
finally
{
Monitor.Exit(_locker);//释放锁
}
}
public void Test()
{
Thread thread1 = new Thread(produce);
Thread thread2 = new Thread(consume);
thread1.Start();
thread2.Start();
}
private int _count;
private object _locker = new object();
}
}
代码中有几个地方需要注意:
1、Monitor.Enter(object)方法是获取锁,Monitor.Exit(object)方法是释放锁,为了避免在获取锁后发
生异常,导致锁无法释放,所以要在finally块中释放锁。经常用的lock(object){}其实是用如下方式实现的:
try{
Monitor.Enter(object);
//do something
}
finally{
Monitor.Exit(object);
}
2、Monitor.Pluse(object)调用后,程序不会立即转向Minitor.Wait(object)执行,
它只是让Wait的线程进入就绪状态,获得执行的可能性。