C#简单理解 Monitor.Wait 与 Monitor.Pulse

这篇具有很好参考价值的文章主要介绍了C#简单理解 Monitor.Wait 与 Monitor.Pulse。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一. 关于 Monitor 控制下线程的三个状态

1. 拥有锁的线程:先行得到锁的线程,得到锁之后,其他线程将进入就绪队列进行等待锁的释放

2. 就绪队列中的线程:等待获取锁

3. 等待队列中的线程:等待显式地被移入就绪队列

二. 方法说明和原理

1.  Monitor.Wait 方法

有两个比较常用的方法重载:

  • Monitor.Wait(Object)

  Object:等待的锁的对象

  功能:释放当前线程所占用的对象锁,并且阻塞当前的线程直到它再次拥有这个锁。

             Releases the lock on an object and blocks the current thread until it reacquires the lock.

  • Monitor.Wait(Object,Int32)

  - Object:等待的锁的对象

  - Int32:线程再次进入就绪队列的等待时长,单位毫秒

  功能:释放当前线程所占用的对象锁,并且阻塞当前的线程直到它再次拥有这个锁。如果指定的时长过去,线程将由等待队列转移到就绪队列。

            Releases the lock on an object and blocks the current thread until it reacquires the lock. If the specified time-out interval elapses, the thread enters the ready queue.

2. Monitor.Wait 方法的主要执行步骤

  • 阻塞当前的线程

  • 将这个线程移动到等待队列中

  • 释放当前的同步锁

3. Monitor.Pulse (Object)

功能:通知一个等待队列中的线程,当前锁的状态被改变。(说白了就是有一个线程可以从等待队列中被移入就绪队列)

Notifies a thread in the waiting queue of a change in the locked object's state.

4. Monitor.PulseAll(Object)

功能:通知所有的等待队列中的线程,当前锁的状态改变。(说白了就是所有的线程可以从等待队列中被移入就绪队列)

Notifies all waiting threads in the waiting queue of a change in the locked object's state.

5. Monitor.Pulse 和 Monitor.PulseAll 的使用写法:

只能由当前获得锁的线程,调用 Monitor.Pulse 和 Monitor.PluseAll 后,使等待队列中的线程转义到就绪队列。

代码一般如下:

lock(obj)
{
    Monitor.Pulse(obj);
}


lock(obj)
{
    Monitor.PulseAll(obj);
}

三. 模拟情形分析

情形一:

1. 假设有5个同时开始的线程,分别是 t1、t2、t3、t4 和 t5,它们将会同时进入一个 lock 区域,如下:

lock(obj)
{
    Monitor.Wait(obj);
}

 2. 由于线程 t1 被第一个处理,进而进入了 lock,它获得锁,此时所有线程的状态:

拥有锁的线程 t1 
就绪队列 t2、t3、t4、t5 
等待队列 

3. 假设线程 t1 运行到了 Monitor.Wait,它将会被从拥有线程锁状态移动到等待队列状态中,于此同时将会释放其拥有的锁,而其它在就绪队列中的线程将有机会获得这个锁:

拥有锁的线程
就绪队列 t2、t3、t4、t5
等待队列 t1

4. 此时假设线程 t2 获取了 t1 释放的锁,它将进入 lock 区域中,此时所有的线程状态如下:

拥有锁的线程 t2
就绪队列 t3、t4、t5
等待队列 t1

5. 接着 t2 在 lock 区域中也会执行 Monitor.Wait ,之后 t2 也会像 t1 一样进入等待队列,重复 1、2 步骤,直至所有的线程 t1、t2、t3、t4、t5 都进入等待队列,如下图:

拥有锁的线程
就绪队列
等待队列 t1、t2、t3、t4、t5

情形二:

如何将上面等待队列中的某一个线程重新变为就绪状态,从而可以再次拿到锁呢?

答:我们可以使用 Monitor.Pulse 来让 t1 线程从等待队列中转移到就绪队列中。

★★ 这里有一个需要注意的地方,就是 " 等待队列 " 是一个队列,满足  " 先进先出 ",所以第一个线程 t1 会被优先释放到就绪队列中。如图是在情形一所有线程都在等待队列中,优先释放哪一个到就绪队列的截图:

C#简单理解 Monitor.Wait 与 Monitor.Pulse

1. 我们在情形一第5点的状态下执行 Monitor.Pulse,此时所有的线程的状态如下:

拥有锁的线程
就绪队列 t1
等待队列 t2、t3、t4、t5

2. 然后,线程 t1 在就绪队列中就会拿到锁,从 Monitor.Wait 的下一句程序开始执行:

拥有锁的线程 t1
就绪队列
等待队列 t2、t3、t4、t5

3. 最后,t1 线程在执行完 lock 区域的剩余部分的代码之后就会退出,同时释放线程锁。于此同时,其它的线程依然被卡在等待队列中等待,如下:

拥有锁的线程
就绪队列
等待队列 t2、t3、t4、t5

 4. 对于 Monitor.PulseAll 将会把所有的等待状态的线程都移到就绪状态的队列中,从而有机会获得锁进行执行。从第3步接着执行 Monitor.PulseAll 之后,所有的线程状态如下:

拥有锁的线程
就绪队列 t2、t3、t4、t5
等待队列

四. 运用

我们来利用 Monitor.Wait 和 Monitor.Pulse 来实现一下 AutoResetEvent 。

代码部分:

/// <summary>
/// 自己的写的AutoResetEvent
/// </summary>
public class AutoResetEventEx
{
    /// <summary>
    /// 内部的设置状态 
    /// true  不等待信号
    /// false 等待信号
    /// </summary>
    private bool _initialState = false;


    /// <summary>
    /// 内部锁
    /// </summary>
    private object _objLock = new object();


    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="initialState">内部的设置状态</param>
    public AutoResetEventEx(bool initialState)
    {
        this._initialState = initialState;
    }


    /// <summary>
    /// 等待一个信号
    /// </summary>
    public void WaitOne()
    {
        if(!this._initialState)
        {
            lock(this._objLock)
            {
                Monitor.Wait(this._objLock);
            }
        }
    }


    /// <summary>
    /// 发送一个信号
    /// </summary>
    public void Set()
    {
        if (!this._initialState)
        {
            this._initialState = true;


            lock (this._objLock)
            {
                Monitor.PulseAll(this._objLock);
            }
        }
    }
}

上端调用部分:

AutoResetEventEx autoResetEventEx = new AutoResetEventEx(true);


Thread th = new Thread(() =>
    {
        Thread.Sleep(10 * 1000);


        Console.WriteLine("- Set -");
        autoResetEventEx.Set();
    });
th.IsBackground = true;
th.Start();


Console.WriteLine("- WaitOne -");
autoResetEventEx.WaitOne();


Console.WriteLine("- Exit -");
Console.ReadKey();

C#简单理解 Monitor.Wait 与 Monitor.Pulse

运行结果:

C#简单理解 Monitor.Wait 与 Monitor.Pulse

五. 性能对比

最后,对比一下 C# 框架的 AutoResetEvent 和手动实现的 AutoResetEventEx:

  • AutoResetEventEx 是 Monitor 实现的,Monitor 采用的是混合锁(用户模式 + 内核模式),不是采用 Win32 API

  • AutoResetEvent 采用的是 Win32 的 API 的 WaitHandle 实现的,所以性能相对比较低(?)文章来源地址https://www.toymoban.com/news/detail-409442.html

到了这里,关于C#简单理解 Monitor.Wait 与 Monitor.Pulse的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • GitHub 仓库 (repository) Pulse - Contributors - Network

    显示该仓库最近的活动信息。该仓库中的软件是无人问津,还是在火热地开发之中,从这里可以一目了然。 显示对该仓库进行过提交的程序员名单。如果您也对该仓库发送过 Pull Request 并且被采纳,那么在这里就能找到自己的名字。左边的数字是程序员的人数。 以图表形式直

    2024年04月10日
    浏览(63)
  • GitHub Pulse 是什么?它是否能衡量 OpenTiny 开源项目的健康程度?

    Pulse 是“脉搏”的意思,就像一个人要有脉搏才能算是一个活人,一个开源项目要有“脉搏”才能算是一个“活”的开源项目,这个单词非常形象地表示了开源项目的健康程度。 脉搏是正常的,开源项目才是健康的。 每个开源项目的 Pulse 数据都是公开的,它位于开源项目代

    2023年04月09日
    浏览(64)
  • 关于Transformer的理解

     关于Transformer,  QKV的意义表示其更像是一个可学习的查询系统,或许以前搜索引擎的算法就与此有关或者某个分支的搜索算法与此类似。   Can anyone help me to understand this image? - #2 by J_Johnson - nlp - PyTorch Forums Embeddings - these are learnable weights where each token(token could be a word, sente

    2024年02月13日
    浏览(28)
  • Java关于反射的理解

    Reflection(反射)是被视为 动态语言 的关键,反射机制允许程序在 执行期借助于 Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。 框架 = 反射 + 注解 + 设计模式。 ➢在运行时判断任意–个对象所属的类 ➢在运行时构造任意-一个类的对象 ➢在运行

    2024年02月10日
    浏览(26)
  • 关于二重积分,三重积分的理解

    最近凭着我狗屎一样的数学啃了好长时间的二重积分和三重积分,记录一下自己对此的理解: 对于积分,只是一重定积分的话可以理解为一块图形的面积,取极小的一块区域计算面积然后遍及到整个图像,对图像区域取极限得到面积就是一重定积分的几何意义。 而将上述方

    2024年02月05日
    浏览(24)
  • 关于Salesforce DevOps的理解

    “DevOps”是一组结合了软件开发 (Dev) 和运营 (Ops) 的实践,可帮助团队更快、更可靠地构建、测试和发布软件。 DevOps 的核心理念包括持续集成(Continuous Integration)、持续交付(Continuous Delivery)、持续部署(Continuous Deployment)、自动化(Automation)、监控与反馈(Monito

    2024年04月11日
    浏览(27)
  • 关于微服务治理的一些理解

    根本意义 其主要目的还是为了解耦,提高灵活性和可扩展性! 参考:https://zhuanlan.zhihu.com/p/462078779 相比单体 单体架构的性能高于微服务架构,微服务的负载能力低于单体架构. 微服务通信之间存在网络IO消耗 ; 单体模块紧耦合,扩展性差; 微服务的敏捷性高,每一个人负责

    2024年02月13日
    浏览(27)
  • C#事件(event)的理解

    遇到一个开发的问题? 面试者:以面向对象的思想实现一下的场景: 猫:Miao一声,紧接着引发了一系列的行为~ Miao:引发了一系列的动作; 从代码层面来说:代码这样写好吗? 猫职责不单一(猫就是猫,他的行为只有Miao一声) 依赖太重,依赖了很多的普通类; 被依赖的类

    2024年03月09日
    浏览(48)
  • 初学者关于ConvLSTM的理解

    最近在着手于使用ConvLSTM进行时空序列预测问题,由于本人刚接触深度学习,很多代码都还理不清,故想到自己通过记录来加深对模型的理解,肯定会有很多问题和不专业的地方,若有网友看见,请不吝指教,谢谢。 ConvLSTM是施博士在《Convolutional LSTM Network: A Machine Learning Ap

    2024年02月11日
    浏览(31)
  • 关于Java注解的一些理解 小结

    目录 1. 常用注解和理解 2. 自定义注解 2.1 案例背景 2.2 设计思路 3 总结 注解在我的理解下,就是代码中的特殊标记,这些标记可以在 编译、类加载、运行时 被读取,并执行相对应的处理。 可能有些抽象,简单来说注解其实在开发中是非常常见的,比如我们在使用各种框架时

    2023年04月23日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包