深入多线程之:用Wait与Pulse模拟一些同步构造的应用详解
发布时间:2021-07-09 06:14:19
你可能在上篇文章中《深入多线程之:双向信号与竞赛的用法分析》注意到了这个模式:两个Waiting 循环都要下面的构造:
lock(_locker)
{
while(!_flag) Monitor.Wait(_locker);
_flag = false;
}
在这里_flag被另一线程设置为true。这是,从作用上讲,这里在模仿AutoResetEvent。如果我们将 _flag = false;去掉,那么我们就得到了一个基本的ManualResetEvent.
让我们使用Wait和Pulse来为ManualResetEvent完成剩余的代码吧。
readonly object _locker = new object();
bool _signal;
void WaitOne()
{
lock (_locker)
{
while (!_signal) Monitor.Wait(_locker);
}
}
void Set()
{
lock (_locker) { _signal = true; Monitor.PulseAll(_locker); }
}
void Reset() { lock (_locker) _signal = false; }
在这里使用PulseAll,是因为可能有很多阻塞的线程。
如果在WaitOne方法中增加_signal=false就可以简单的模拟AutoResetEvent.例如:
void WaitOne()
{
lock (_locker)
{
while (!_signal) Monitor.Wait(_locker);
_signal = false; //实现自动关闭功|能
}
}
然后在Set方法中,将PulseAll修改为Pulse
Lock(_locker) {_signal = true; Monitor.Pulse(_locker);}
如果使用的是int类型的_signal 标志,那么我们可以得到一个最基本的Semaphore.
Waiting Queues and PulseAll
当多余一个线程在同一个对象上面等待的时候,一个 “等待队列(waiting queue)” 就形成了。
每一次调用Pulse都会释放在”等待队列”头部的一个线程。下面的图形象的展示了这一点:
线程调用Monitor.Enter 进入ReadyQueue. 等待获取锁,成功获取锁后,如果正常的执行,那么之后会调用Monitor.Exit退出,
否则如果获取了锁之后发现需要等待其他的线程或者是其他阻塞条件,那么调用Wait方法,就进入了等待队列,
当等待的线程完成并调用Pulse后,处在WaitingQueue头部的线程就被 Pulse了,等待CPU调度 。之后再次进入Ready Queue,重新获取锁。
Countdown
借助Wait和Pulse,我们可以实现CountdownEvent的主要功能。例如:
class Countdown
{
object _locker = new object();
int _value; //使用_value来计数
public Countdown() { }
public Countdown(int initialCount) { _value = initialCount; }
public void Singnal() { AddCount(-1); } //将计数减一
public void AddCount(int amount)
{
lock (_locker)
{
_value += amount; //将计数增加或减少
if (_value <= 0) Monitor.PulseAll(_locker);//如果value<=0,说明所有等待的任务都完成了。
}
}
public void Wait()
{
lock (_locker)
{
//只要计数 > 0 就等待。
while (_value > 0)
{
Monitor.Wait(_locker);
}
}
}
}
这和我们上次的代码几乎一致,只是这次我们的阻塞条件基于一个整型_value标志。


猜你喜欢
- 一.理论准备KMP算法为什么比传统的字符串匹配算法快?KMP算法是通过分析模式串,预先计算每个位置发生不匹配的时候,可以省去重新匹配的的字符
- java 中 System.out.println()和System.out.write()的区别.这两个函数一个是System
- 本文实例讲述了JavaMail实现邮件发送的方法。分享给大家供大家参考。具体如下:下载 activation.jar 和 mail.jar配
- 前言大家对AOP应该都不陌生, 就算没有用过也肯定听说过,切面编程一直是一个热点的话题,AOP即Aspect Oriented Progra
- 前言数据库访问是web应用必不可少的部分。现今最常用的数据库ORM框架有Hibernate与Mybatis,Hibernate貌似在传统IT
- AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Map
- 字符串的操作是C#程序设计中十分重要的一个组成部分,本文就以实例形式展现了C#实现移除字符串末尾指定字符的方法。相信对大家学习C#程序设计有
- 本文实例讲述了C#使用Ado.net读取Excel表的方法。分享给大家供大家参考。具体分析如下:微软NET提供了一个交互的方法,通过使用AD
- 1. 实现原理将key映射到 2^32 - 1 的空间中,将这个数字的首尾相连,形成一个环计算节点(使用节点名称、编号、IP地址)的hash
- 本文实例为大家分享了java代码获取新浪微博应用的access token的具体代码,供大家参考,具体内容如下package test;im
- 动态表单的含义是不要手动定义,直接在配置文件中进行定义。1.手动进行定义<form-beans > <form-bean
- 集合>队列Queue>创建队列System.Collections.Queue类提供了四种重载构造函数。using System
- @ModelAttribute在父类、子类的执行顺序被 @ModelAttribute 注解的方法会在Controller每个方法执行之前都
- 前言这个也是Java实验课程的一个作业,和Java实现简单的图形界面计算器一起做的,因为以前没有做过GUI编程,所以做的非常简陋,还有很多B
- 本文会先介绍通用 Mapper 的简单原理,然后使用最简单的代码来实现这个过程。基本原理通用 Mapper 提供了一些通用的方法,这些通用方
- 本文实例讲述了Android开发使用HttpURLConnection进行网络编程。分享给大家供大家参考,具体如下:——HttpURLCon
- 一、在java中遍历一个文件夹里边的所有文件,可以有两种方式:1.递归遍历,通常也是开发者第一时间能想到的方法,递归遍历的优点是:实现起来相
- 😜shape属性详解<?xml version="1.0" encoding="utf-8"?
- 前端采用layui框架,讲解多文件上传的完整实现功能。前端html重点代码如下:<div class="layui-form
- 这里以list为介绍:private static readonly T[] s_emptyArray = new T[0];public