当前位置: 移动技术网 > IT编程>开发语言>c# > 深入多线程之:双向信号与竞赛的用法分析

深入多线程之:双向信号与竞赛的用法分析

2019年07月18日  | 移动技术网IT编程  | 我要评论
双向信号和竞赛(two-way signaling and races) monitor.pulse方法的一个重要特性是它是异步执行的,这意味着调用pulse方法

双向信号和竞赛(two-way signaling and races)
 
monitor.pulse方法的一个重要特性是它是异步执行的,这意味着调用pulse方法并不会阻塞自己等待monitor.pulse返回。如果任何一个线程在pulsed 对象上等待,它是不会阻塞的,换句话说,调用monitor.pulse对程序不会有什么作用,你可以认为monitor.pulse方法被忽略了。
这样pulse提供了一个单向通信:一个 pulsing线程悄悄的向一个waiting 线程发送信号。
pulse并不会返回一个值来告诉你waiting线程是否收到信号。

但是有时候我们需要知道waiting线程是否受到信号,例如下面的例子:

复制代码 代码如下:

class race
    {
        static readonly object _locker = new object();
        static bool  _go;
        public static void mainthread()
        {
            new thread(saysomething).start();
            for (int i = 0; i < 5; i++)
            {
                lock (_locker)
                {
                    _go = true;
                    monitor.pulseall(_locker); //通知等待的队列
                }
            }
        }
        static void saysomething()
        {
            for (int i = 0; i < 5; i++)
            {
                lock (_locker)
                {
                    while (!_go) monitor.wait(_locker); //如果_go 为false,那么开始阻塞。
                    _go = false;
                    console.writeline("wassup?");
                }
            }
        }
    }

期待的输出:
wassup?
wassup?
wassup?
wassup?
wassup?

实际的输出:

wassup? (线程等待)
 
在saysomething方法中,for循环执行到while,此时_go为false,所以monitor.wait开始等待。在mainthread中,for循环设置_go为true。然后pulseall.但是pulseall方法是异步的。
所以在saysomething线程被唤醒前,mainthread中的for循环可能已经执行完毕。所以saysomething方法中的第一个wait线程收到消息词是_go为true,所以往下执行,再次将_go字段设置为false。输出”wassup?”,但是下次循环由于_go为false,所以需要再次wait.所以实际的输出打印了一个wassup,然后开始等待。
我们需要主线程在每一次迭代中如果worker仍然在执行上一个任务,那么主线程阻塞。等到worker执行完毕,那么主线程恢复执行,然后执行迭代。

我们可以增加一个_ready 标志,从而控制主线程在设置_go 标志之前worker线程已经ready了。也就是说主线程在设置_go之前,会等待worker完成任务,然后等待worker将ready设为true,当worker将ready设置为true后,通过pulse来通知主线程。
复制代码 代码如下:

class race
    {
        static readonly object _locker = new object();
        static bool _ready, _go;
        public static void mainthread()
        {
            new thread(saysomething).start();
            for (int i = 0; i < 5; i++)
            {
                lock (_locker)
                {
                    while (!_ready) monitor.wait(_locker); //如果worker的ready为false,则等待worker。
                    _ready = false; //重置标志
                    _go = true;
                    monitor.pulseall(_locker);
                }
            }
        }
        static void saysomething()
        {
            for (int i = 0; i < 5; i++)
            {
                lock (_locker)
                {
                    _ready = true; //将ready设置为true
                    monitor.pulseall(_locker); //通知主线程,worker已经ready了,可以执行任务了。
                    while (!_go) monitor.wait(_locker);
                    _go = false;
                    console.writeline("wassup?");
                }
            }
        }
    }

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网