Wait和Pulse

WaitPulse

 

前言

 

好几天没有关注C#,今天突然想起来了,觉得代码这个玩意不能不写,不写容易手生,还是能写一点算一点.过几天可能会写点设计模式之类的文章,还有开始学习数据库的知识.大三争取过的充实一点.争取每天都有进步,一天比一天优秀.加油!

 

 

 

引入

 

Signaling with Wait and Pulse(等待和暂停的信号)

 

前面说过等待事件句柄(调用Wait的线程在没有收到另一个线程的通知前会一直阻塞).

 

Monitor借助它的静态方法Wait,Pulse,PulseAll提供了一个更给力的信号构造,使用这些方法和lock语句,你可以自己实现AutoResetEvent,ManuResetEventSemaphore.甚至WaitHandleWaitAllWaitAny方法了.

 

 

 

怎样使用WaitPulse?

 

1:定义一个同步对象,例如:

Readonly object _locker=new object();


2:定义自己的阻塞条件中的字段.

bool _go或者int _semaphoreCount;


3:当你想要阻塞的时候,包含下面的代码

lock(_locker)
while(<阻塞条件>)//比如while(_go==false)
{
Monitor.Wait(_locker);//满足阻塞条件,开始阻塞
}


4:当想要改变阻塞条件的时候,包含下面的代码:

</pre><pre name="code" class="csharp">lock(_locker)
{
//<更改阻塞条件中的字段>,比如_go=true;
Monitor.Pulse(_locker);//或者:Monitor.PulseAll(_locker);//通知等待队列中的线程锁定对象状态的更改.
}


 

这个模式可以让你随时随地等待线程.下面这个案例,worker线程在_go字段编程true之前会一直等待.

 

   

 class Program
    {
        static readonly object _locker = new object();
        static bool _go;
        static void Main(string[] args)
        {
            new Thread(Work).Start();
            Console.ReadLine();//等待用户输入
 
            lock (_locker)
            {
                _go = true;
                Monitor.Pulse(_locker);//通知等待队列
            }
 
        }
        static void Work()
        {
            lock (_locker)
            {
                while (!_go)//只要_go字段是false,就等待
                {
                    Monitor.Wait(_locker);//在等待的时候,锁已经被释放了
                }
            }
            Console.WriteLine("被唤醒了!");
        }
    }
 

为了线程安全,确保所有共享的字段在读取的时候都加锁了.

 

Work方法会一直阻塞,等待_go字段编程true,Monitor.Wait方法按照顺序的做了以下的操作:

 

1.释放锁_locker;

2.阻塞锁,知道_locker”Pulsed”;

3.重新再_locker上获取锁,如果锁已经被其他线程获得,那么现成开始阻塞,直到锁变得可用为止.

 

lock(_locker)
{
while(!_go)//释放锁
Monitor.Wait(_locker);//已经重新获取了锁.
}
 


如果我们抛弃了该模式,例如移除while循环,_go字段和ReadLine方法等:

 

 

   class Program
    {
        static readonly object _locker = new object();
        //static bool _go;
        static void Main(string[] args)
        {
            new Thread(Work).Start();           
 
            lock (_locker)
            {                
                Monitor.Pulse(_locker);//通知等待队列
            }
 
        }
        static void Work()
        {
            lock (_locker)
            {
                Monitor.Wait(_locker);//在等待的时候,锁已经被释放了
            }
            Console.WriteLine("被唤醒了!");
        }
    }
 

那么程序的结果将会出现不确定的结果,有可能显示”被唤醒了”.也可能不显示.

 

主要原因是主线程和Work线程之间存在着竞争关系,如果Wait方法先执行,那么可以正常显示,但是如果Pulse方法先执行,Pulse就会丢失,Work线程就会永远的等待,这种行为和AutoResetEvent不同,AutoResetEventSet方法有一种记忆的效果,所以即使它在WaitOne方法前调用,他仍然有效.

 

但是Pulse没有记忆功能,因为你希望自己实现记忆功能,就像我们之前使用_go标识一样,这就是为什么WaitPulse是通用的原因:使用一个boolean标识,我们可以实现AutoResetEvent的功能,使用一个integer标识,我们可以实现CountdownEvent,Semaphore,使用更复杂的结构,我们可以歇一歇更复杂的狗杂,例如生产/消费者队列.

你可能感兴趣的:(C#)