欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

深入多线程之:Wait与Pulse的使用详解

程序员文章站 2023-12-20 10:00:40
signaling with wait and pulse(等待和暂停的信号) 早期谈论过等待事件句柄(调用wait的线程在没有收到另一个线程的通知前会一直阻塞)。 m...

signaling with wait and pulse(等待和暂停的信号)

早期谈论过等待事件句柄(调用wait的线程在没有收到另一个线程的通知前会一直阻塞)。

monitor借助它的静态方法wait,pulse,pulseall提供了一个更给力的信号构造,使用这些方法和lock语句,你可以自己实现autoresetevent,manualresetevent和semaphore。甚至waithandle的waitall和waitany方法了。

怎样使用wait 和pulse ?

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

  readonly object _locker=new object();

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

  bool _go 或者 int _semaphorecount;

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

  lock(_locker)

         while(<阻塞条件 >) //比如while (_go ==false)

                   monitor.wait(_locker);    //满足阻塞条件,开始阻塞。

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

     lock(_locker)

{

    //<更改阻塞条件中的字段>,比如_go=true;

         monitor.pulse(_locker); //或者: monitor.pulseall(_locker); //通知等待队列中的线程锁定对象状态的更改。

}

这个模式可以让你随时随地等待线程。下面是一个例子,worker线程在_go 字段变成true之前会一直等待。

复制代码 代码如下:

static readonly object _locker = new object();
        static bool _go;

        internal static void main()
        {
            new thread(work).start(); //新线程会被阻塞,因为_go == false
            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方法等:
复制代码 代码如下:

static object _locker = new object();

        internal static void main()
        {
            new thread(work).start();
            lock (_locker) monitor.pulse(_locker);
        }

        static void work()
        {
            lock (_locker) monitor.wait(_locker);
            console.writeline("被唤醒了");
        }


那么程序运行的结果又如何呢?

实际上输出是不确定的,有可能你不能显示“被唤醒了”。

主要原因是主线程和work线程之间存在着竞争关系,如果wait方法先执行,那么可以正常显示,但是如果pulse方法先执行,pulse就会丢失,worker线程就会永远的等待。这种行为和autoresetevent不同,autoresetevent的set方法有一种记忆的效果,所以即使它在waitone方法前调用,它仍然有效。


但是pulse没有记忆功能,因为你希望自己实现记忆功能,就像我们之前使用_go 标志一样,

这就是为什么wait和pulse是通用的原因:使用一个boolean 标志,我们可以实现autoresetevent的功能,使用一个integer标志,我们可以实现 countdownevent,semaphore.使用更复杂的结构,我么可以写一些更复杂的构造。

上一篇:

下一篇: