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方法)。就这样生命不息,循环往复。

 

最新文章

  1. 修改sql数据库文件 物理文件名称
  2. maven最齐全配置pom.xml
  3. Magento架构师的笔记-----Magento显示当前目录的父分类和子分类的分类名
  4. C++中回调函数(CallBack)的使用
  5. ssl选购
  6. 【转】no resource found @integer/google_play_services_version whats this?
  7. MFC 点击改变文本 加法计时器 伸缩窗体
  8. Oracle 表三种连接方式(sql优化)
  9. postman+jenkins+newman做接口测试的持续集成
  10. 读Zepto源码之Ajax模块
  11. Redis学习——详解Redis配置文件(三)
  12. Dynamics CRM 日常使用JS整理(二)
  13. JS学习笔记Day15
  14. GitHub 优秀的 Android 开源项目 (精品)
  15. android高级页面效果集锦
  16. LeetCode 696 Count Binary Substrings 解题报告
  17. 深入理解Java的堆内存和线程内存
  18. USB基础知识概论(版本:v0.9.2)
  19. 微信小程序 - 自定义弹窗组件
  20. restfull 风格 参考 https://blog.csdn.net/jaryle/article/details/52141097

热门文章

  1. Python web后端接收到的json数据有前端格式的布尔值 true false
  2. jzoj5875
  3. PHP之旅8 URL与表单
  4. 如何通过Python暴力破解网站登陆密码
  5. java 中 enum 枚举的使用
  6. 用absolute进行页面的自适应布局
  7. Python小白学习之路(二十一)—【迭代器】
  8. RPC理解
  9. 多项式求逆元详解+模板 【洛谷P4238】多项式求逆
  10. Linux CentOS7系统探索