ManualResetEvent是C#中一个比较常用的工具,可用于线程间通信,实现一种类似信号量的功能(不知道我这样描述是否恰当,有可能不是“类似”,而“就是”通过信号量来实现的,因为我也是最近才知道这个类,以前一直不知道,哈哈。如果有哪位清楚的话,请给我解惑。)。

先了解一下ManualResetEvent的基本用法:

1、初始化:public ManualResetEvent(bool initialState);

  ManualResetEvent的构造方法有个bool型参数,当为true时,则表示有信号,为false时,则表示无信号。这个怎么理解呢?我们接着看ManualResetEvent3个基本方法中的WaitOne方法。

2、WaitOne方法:WaitOne方法有几种4种重载,我在这里只对它的功能进行分析。

  WaitOne方法,顾名思义,它会具有一种等待的功能,也就是线程阻塞。这里的阻塞功能是有条件的,当无信号时,它是阻塞的,有信号时,它将无任何阻塞,被执行时就直接跳过了(这个从逻辑上应该挺好理解:当有信号需要处理时,需要立即处理,没有任何信号时,就当然要等一等了)。所以,回顾到1,当初始化ManualResetEvent时,initialState为false,WaitOne将会有阻塞效果,否则,没有阻塞效果。

3、Set方法:将ManualResetEvent对象的信号状态设为有信号状态,这个时候WaitOne如果正在阻塞中的话,将会立即终止阻塞,向下继续执行。而且这个状态一直不变的话,每次执行到WaitOne都将无任何阻塞。

4、Reset方法:将ManualResetEvent对象的信号状态设为无信号状态,当下次执行到WaitOne时,又将重新开始阻塞。

呵呵,按我个人理解,ManualResetEvent得几个方法的功能大致就这个意思。嗯,口说无凭,代码才是王道。接下来我用一个生产消费模型的例子来给大家班门弄斧一下!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;namespace ThreadTest
{class Program{static void Main(string[] args){new ProductAndCostTester();}}/// <summary>/// 生产消费模型/// </summary>public class ProductAndCostTester{/// <summary>/// 生产线1线程/// </summary>private Thread _producterThread1;/// <summary>/// 生产线2线程/// </summary>private Thread _producterThread2;/// <summary>/// 消费线线程/// </summary>private Thread _costerThread;/// <summary>/// 产品列表/// </summary>private List<int> _goodList;/// <summary>/// ManualResetEvent实例/// </summary>private ManualResetEvent _mre;public ProductAndCostTester(){_goodList = new List<int>();_mre = new ManualResetEvent(false);//false初始化状态为无信号,将使WaitOne阻塞
_producterThread1 = new Thread(Product1);_producterThread1.Name = "Productor1";_producterThread1.Start();_producterThread2 = new Thread(Product2);_producterThread2.Name = "Productor2";_producterThread2.Start();_costerThread = new Thread(Cost);_costerThread.Name = "Costor";_costerThread.Start();}/// <summary>/// 生产线1/// </summary>void Product1(){while (true){Console.WriteLine(Thread.CurrentThread.Name + ":" + DateTime.Now.ToString("HH:mm:ss"));for (int i = 0; i < 3; i++){_goodList.Add(1);}_mre.Set();//表示有信号了,通知WaitOne不再阻塞
Thread.Sleep(8000);}}/// <summary>/// 生产线2/// </summary>void Product2(){while (true){Console.WriteLine(Thread.CurrentThread.Name + ":" + DateTime.Now.ToString("HH:mm:ss"));for (int i = 0; i < 6; i++){_goodList.Add(1);}_mre.Set();//表示有信号了,通知WaitOne不再阻塞
Thread.Sleep(10000);}}/// <summary>/// 消费线/// </summary>void Cost(){while (true){if (_goodList.Count > 0){Console.WriteLine("Cost " + _goodList.Count + " at " + DateTime.Now.ToString("HH:mm:ss"));_goodList.Clear();_mre.Reset();//重置为无信号了,使WaitOne可以再次阻塞
                }else{Console.WriteLine("No cost at " + DateTime.Now.ToString("HH:mm:ss"));_mre.WaitOne();//如果没有可消费的产品,即无信号,则会阻塞
                }}}}
}

这个代码是可以直接运行的,我就不再用附件了。嗯,下面我来简单讲解一下我这个代码想表达什么:

这里有3个线程,2条生产线和1条消费线。2条生产线同时进行,但是可能生产的速度不一致(这里一个8s/次,一个10s/次)。而另外一个消费线程也是与生产线同时运行的,我想实现一个目标:每当有产品可以消费时,我将立即消费,不想有任何延迟。

按照以前最简单常用的思路,就是让消费线程每次运行sleep一下,但是这个需要循环时间足够短、循环频率足够快才行,频率至少要高于任意一个生产线程,即sleep时间小于生产线程中sleep时间的最小值。

如果这样实现,代码从逻辑来讲是没有任何问题的,但是效率太低了,而且可能遭遇麻烦。假设这样一种情况,如果生产线程的生产频率是不固定的(不像我们这固定sleep几秒钟,这个在真实情况中是存在的),有时候1小时才生产一次,有时候100毫秒生产一次(笑,这个比较极端啊),那么我们至少需要将消费线程的sleep时间低于100毫秒才行。这样的话当生产线程1个小时一次的时候是不是也太浪费了,基本上消费线程在空转。

所以嘛,才有了我这样一个代码,我的消费线程每次循环都会检查已经生产出来的产品数量,当有产品可供消费的时候,我就一次消费光,并且提醒:“已经没有可消费的产品了,下次可能需要等等了!”(调用Reset方法),那么下次循环时,检查到果然没有产品了,那么就将等待了(WaitOne方法阻塞)。这时候消费线程就会完全停在这了,不会每次都空转,是不是比较人性化?呵呵。

接下来,任意一个生产线程如果生产出新的产品,就将会通知消费线程:“嘿,伙计,你要的东西来了,快醒醒吧!”(调用Set方法),这样消费线程就会立马继续运行(WaitOne方法将会继续向下执行,并且在再次Reset前,它都不会再阻塞了)。当然,消费线程得下次循环将检测到有产品可供消费了,它又会将产品消费完,并且又提醒:“已经没有可消费的产品了,下次可能需要等等了!”(调用Reset方法)。就这样生命不息,循环往复。

转载于:https://www.cnblogs.com/modify/archive/2013/01/10/2855014.html

C# ManualResetEvent的理解和用法相关推荐

  1. python模块之HTMLParser之穆雪峰的案例(理解其用法原理)

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #python模块之HTMLParser之穆雪峰的案例(理解其用法原理) #http://www.cnblog ...

  2. string_view理解与用法(二)

    以前写了<string_View理解与用法(一)>和<详解C++17下的string_view>,请参考. 本篇文章从string_view引入的背景出发,依次介绍了其相关的知 ...

  3. Promise async/await的理解和用法

    Promise && async/await的理解和用法 为什么需要promise(承诺)这个东西 在之前我们处理异步函数都是用回调这个方法,回调嵌套的时候会发现 阅读性 和 调试 的 ...

  4. php yield 个人小解_PHP5.5新特性之yield理解与用法实例分析

    本文实例讲述了PHP5.5新特性之yield理解与用法.分享给大家供大家参考,具体如下: yield生成器是php5.5之后出现的,yield提供了一种更容易的方法来实现简单的迭代对象,相比较定义类实 ...

  5. 对于EnterCriticalSection和LeaveCriticalSection的理解和用法

     对于EnterCriticalSection和LeaveCriticalSection的理解和用法 2015年08月26日 11:58:08 阅读数:4966  线程锁的概念函数EnterC ...

  6. bind()、call()、apply()理解及用法

    apply和call都是为了改变某个函数运行时的上下文而存在的(就是为了改变函数内部this的指向),Function对象的方法,每个函数都能调用: 使用apply或call方法,其运行的上下文指向第 ...

  7. Pytorch forward()的简单理解与用法

    1.基本用法 在pytorch中,使用torch.nn包来构建神经网络,我们定义的网络继承自nn.Module类.而一个nn.Module包含神经网络的各个层(放在__init__里面)和前向传播方式 ...

  8. 对AutoResetEvent和ManualResetEvent的理解

    一.作用 AutoResetEvent和ManualResetEvent可用于控制线程暂停或继续,拥有重要的三个方法:WaitOne.Set和Reset. 这三个方法的官方定义并不好理解,什么终止.非 ...

  9. 个人对AutoResetEvent和ManualResetEvent的理解

    仅个人见解,不对之处请指正,谢谢. 一.作用 AutoResetEvent和ManualResetEvent可用于控制线程暂停或继续,拥有重要的三个方法:WaitOne.Set和Reset. 这三个方 ...

最新文章

  1. Amazon Aurora是如何设计原生云关系型数据库的?
  2. 42.虚拟内存如何设置:
  3. Centos7安装mariadb galera cluster数据库集群 详解
  4. 你应该更新的Java知识
  5. RocketMQ类关系图之NameServer
  6. 使用Fiddler进行iOS APP的HTTP/HTTPS抓包
  7. [react] 使用react写一个todo应用,说说你的思路
  8. 机器学习算法总结--朴素贝叶斯
  9. 巧用“傍术”选择陈列点
  10. thymeleaf 的 th:onclick js方法入参
  11. 电脑格式化后需要重装系统吗_重装系统后c盘文件丢失,电脑重装系统后c盘文件能恢复吗...
  12. android studio 布局设计,Andriod界面设计适配和Android Studio中的资源
  13. 9.2 配置rsync+inotify实时同步
  14. win7计算机开机启动项设置,开机启动项,教您Win7开机启动项怎么设置
  15. WPS简历模板的图标怎么修改_个人简历怎么做?个人求职简历模板
  16. 17-03-21 课堂笔记
  17. Python3.6支付宝账单爬虫
  18. Tensorflow中axis的理解
  19. 大数据分析与应用(中级) 数据挖掘概念及流程
  20. 数据库设计多表关系、范式

热门文章

  1. C# WINFORM的自动更新程序
  2. [LeetCode-JAVA] The Skyline Problem
  3. 解析IOS控制台利用GDB命令查看报错堆栈
  4. 《计算机网络》谢希仁第五版考试重点整理
  5. 中秋应景诗词歌赋赏析
  6. c语言将程序写为动态库,VS下生成C程序静态库(LIB)及动态库(DLL)的方法
  7. matlab远程桌面访问:License Manager Error - 103
  8. C语言上证指数运行源码,个股对比上证指数公式源码
  9. ascll 和gbk,utf-8的简介
  10. django 打开的html css_Django分页完整示例