Monitor类

      Monitor类通过向单个线程授予对象锁来控制对对象的访问。对象锁提供限制访问临界区的能力。当一个线程拥有对象的锁时,其他任何线程都不能获取该锁。还可以使用 Monitor 来确保不会允许其他任何线程访问正在由锁的所有者执行的应用程序代码节,除非另一个线程正在使用其他的锁定对象执行该代码。

      通过对lock关键字的分析我们知道,lock就是对Monitor的Enter和Exit的一个封装,而且使用起来更简洁,因此Monitor类的Enter()和Exit()方法的组合使用可以用lock关键字替代。

      另外Monitor类还有几个常用的方法:

      TryEnter()能够有效的解决长期死等的问题,如果在一个并发经常发生,而且持续时间长的环境中使用TryEnter,可以有效防止死锁或者长时间的等待。比如我们可以设置一个等待时间bool gotLock = Monitor.TryEnter(myobject,1000),让当前线程在等待1000秒后根据返回的bool值来决定是否继续下面的操作。

      Wait()释放对象上的锁以便允许其他线程锁定和访问该对象。在其他线程访问对象时,调用线程将等待。脉冲信号用于通知等待线程有关对象状态的更改。

      Pulse(),PulseAll()向一个或多个等待线程发送信号。该信号通知等待线程锁定对象的状态已更改,并且锁的所有者准备释放该锁。等待线程被放置在对象的就绪队列中以便它可以最后接收对象锁。一旦线程拥有了锁,它就可以检查对象的新状态以查看是否达到所需状态。

      注意:Pulse、PulseAll和Wait方法必须从同步的代码块内调用。

      我们假定一种情景:妈妈做蛋糕,小孩有点馋,妈妈每做好一块就要吃掉,妈妈做好一块后,告诉小孩蛋糕已经做好了。下面的例子用Monitor类的Wait和Pulse方法模拟小孩吃蛋糕的情景。


    //
仅仅是说明Wait和Pulse/PulseAll的例子
   
//逻辑上并不严密,使用场景也并不一定合适

    class
MonitorSample
    {
       
private int n = 1//生产者和消费者共同处理的数据

        private int max = 10000
;

       
private object monitor = new object
();

       
public void
Produce()
        {
           
lock
(monitor)
            {
               
for (; n <= max; n++
)
                {
                    Console.WriteLine(
"妈妈:第" + n.ToString() + "块蛋糕做好了"
);
                   
//
Pulse方法不用调用是因为另一个线程中用的是Wait(object,int)方法
                   
//
该方法使被阻止线程进入了同步对象的就绪队列
                   
//
是否需要脉冲激活是Wait方法一个参数和两个参数的重要区别
                   
//
Monitor.Pulse(monitor);
                   
//
调用Wait方法释放对象上的锁并阻止该线程(线程状态为WaitSleepJoin)
                   
//
该线程进入到同步对象的等待队列,直到其它线程调用Pulse使该线程进入到就绪队列中
                   
//
线程进入到就绪队列中才有条件争夺同步对象的所有权
                   
//如果没有其它线程调用Pulse/PulseAll方法,该线程不可能被执行

                    Monitor.Wait(monitor);
                }
            }
        }

       
public void
Consume()
        {
           
lock
(monitor)
            {
               
while (true
)
                {
                   
//
通知等待队列中的线程锁定对象状态的更改,但不会释放锁
                   
//
接收到Pulse脉冲后,线程从同步对象的等待队列移动到就绪队列中
                   
//注意:最终能获得锁的线程并不一定是得到Pulse脉冲的线程

                    Monitor.Pulse(monitor);
                   
//
释放对象上的锁并阻止当前线程,直到它重新获取该锁
                   
//如果指定的超时间隔已过,则线程进入就绪队列

                    Monitor.Wait(monitor,1000
);
                    Console.WriteLine(
"孩子:开始吃第" + n.ToString() + "块蛋糕"
);
                }
            }
        }

       
static void Main(string
[] args)
        {
            MonitorSample obj
= new
MonitorSample();
            Thread tProduce
= new Thread(new
ThreadStart(obj.Produce));
            Thread tConsume
= new Thread(new
ThreadStart(obj.Consume));
           
//Start threads.

            tProduce.Start();
            tConsume.Start();

            Console.ReadLine();
        }
    }

 

      这个例子的目的是要理解Wait和Pulse如何保证线程同步的,同时要注意Wait(obeject)和Wait(object,int)方法的区别,理解它们的区别很关键的一点是要理解同步的对象包含若干引用,其中包括对当前拥有锁的线程的引用、对就绪队列(包含准备获取锁的线程)的引用和对等待队列(包含等待对象状态更改通知的线程)的引用

posted on 2009-06-24 17:36  瞌睡虫  阅读(659)  评论(0编辑  收藏  举报