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

时间:2022-02-17 06:54:49 

双向信号和竞赛(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?");
                }
            }
        }
    }


标签:双向信,与竞赛
0
投稿

猜你喜欢

  • SpringBoot 使用 FTP 操作文件的过程(删除、上传、下载文件)

    2021-07-26 10:40:05
  • C#生成Word文件(图片、文字)

    2023-03-28 04:07:58
  • Java的设计模式编程中迪米特法则的应用示例

    2022-10-29 01:34:35
  • Winform项目中TextBox控件DataBindings属性

    2023-03-29 15:25:15
  • SprintBoot深入浅出讲解场景启动器Starter

    2023-11-24 20:58:58
  • java 类加载机制和反射详解及实例代码

    2023-11-30 06:42:20
  • AOP从静态代理到动态代理(Emit实现)详解

    2023-11-27 14:49:56
  • Java压缩文件工具类ZipUtil使用方法代码示例

    2022-11-26 02:21:32
  • RocketMQ消息过滤与查询的实现

    2023-06-26 10:04:25
  • Java规则引擎Easy Rules的使用介绍

    2023-05-04 01:42:57
  • 基于Java编写串口通信工具

    2022-11-30 09:25:34
  • java ssm框架实现分页功能的示例代码(oracle)

    2021-10-31 01:14:40
  • MVC框架自定义实现过程

    2021-07-24 03:31:47
  • C#中一些你可能没用过的调试窗口的方法

    2022-08-07 09:01:16
  • java中的session对象及其常用方法小结

    2023-11-25 02:31:33
  • 全面解析Java中的引用类型

    2022-07-15 23:48:07
  • Java实现五子棋的基础方法

    2021-07-11 12:32:08
  • Java动态显示文件上传进度实现代码

    2022-09-09 20:18:17
  • VC++时钟函数

    2021-06-17 10:07:51
  • WindowsForm实现TextBox占位符Placeholder提示功能

    2023-03-18 12:57:44
  • asp之家 软件编程 m.aspxhome.com