一、ManualResetEvent

该对象有两种信号量状态True和False。构造函数设置初始状态。简单来说,

  • 如果构造函数由true创建,则第一次WaitOne()不会阻止线程的执行,而是等待Reset后的第二次WaitOne()才阻止线程执行。
  • 如果构造函数有false创建,则WaitOne()必须等待Set()才能往下执行。

  一句话总结就是:是否忽略第一次阻塞。

  方法如下:

  • WaitOne:该方法用于阻塞线程,默认是无限期的阻塞,支持设置等待时间,如果超时就放弃阻塞,不等了,继续往下执行;
  • Set:手动修改信号量为True,也就是恢复线程执行;
  • ReSet:重置状态;
    class Program{//一开始设置为false才会等待收到信号才执行static ManualResetEvent mr = new ManualResetEvent(false);public static void Main(){Thread t = new Thread(Run);//启动辅助线程t.Start();//等待辅助线程执行完毕之后,主线程才继续执行Console.WriteLine("主线程一边做自己的事,一边等辅助线程执行!" + DateTime.Now.ToString("mm:ss"));mr.WaitOne();Console.WriteLine("收到信号,主线程继续执行" + DateTime.Now.ToString("mm:ss"));Console.ReadKey();}static void Run(){//模拟长时间任务Thread.Sleep(3000);Console.WriteLine("辅助线程长时间任务完成!" + DateTime.Now.ToString("mm:ss"));mr.Set();}}

  输出结果如下:

  

  在思维上,这个东西可以有两种用法,一种是让主线程等待辅助线程,一种是辅助线程等待主线程。

  但无论怎么用,都是让一个线程等待或唤醒另外一个线程。

  Reset

    class Program{//一开始设置为false,当遇到WaitOne()时,需要Set()才能继续执行static ManualResetEvent mr = new ManualResetEvent(false);public static void Main(){Thread t = new Thread(Run);t.Start();mr.WaitOne();Console.WriteLine("第一次等待完成!" + DateTime.Now.ToString("mm:ss"));mr.Reset();     //重置后,又能WaitOne()啦mr.WaitOne(3000);Console.WriteLine("第二次等待完成!" + DateTime.Now.ToString("mm:ss"));Console.ReadKey();}static void Run(){mr.Set();Thread.Sleep(2000);mr.Set();}}

  输出如下:

  

  如果以上代码不使用Reset,则直接输出第二次等待完成,而不会等待2秒。

二、AutoResetEvent

  AutoResetEvent与ManualResetEvent的区别在于AutoResetEvent 的WaitOne会改变信号量的值为false,让其等待阻塞。

  比如说初始信号量为True,如果WaitOne超时信号量将自动变为False,而ManualResetEvent则不会。

  第二个区别:

  • ManualResetEvent:每次可以唤醒一个或多个线程;
  • AutoResetEvent:每次只能唤醒一个线程;
    class Program{static AutoResetEvent ar = new AutoResetEvent(true);public static void Main(){Thread t = new Thread(Run);t.Start();bool state = ar.WaitOne(1000);Console.WriteLine("当前的信号量状态:{0}", state);state = ar.WaitOne(1000);Console.WriteLine("再次WaitOne后现在的状态是:{0}", state);state = ar.WaitOne(1000);Console.WriteLine("再次WaitOne后现在的状态是:{0}", state);Console.ReadKey();}static void Run(){Console.WriteLine("当前时间" + DateTime.Now.ToString("mm:ss"));}}

  输出如下:

  

  假如要用ManualResetEvent实现上面同样的效果,Run方法就不用手动Reset()了,AutoResetEvent保证后续每个WaitOne()都有效:

    static void Run(){//线程开始执行时待命,收到信号才动身mr.WaitOne();//我想让辅助线程暂停3秒mr.WaitOne(3000);//我想让辅助线程暂停,10后由主线程再次唤醒mr.WaitOne();}

  少了手动Reset()代码。

  2014-11-13

  Workflow 4.5用的就是这个东西,因为对信号量这个东西不熟,可算吃了大亏。

三、Semaphore

  用于控制线程的访问数量,默认的构造函数为initialCount和maximumCount,表示默认设置的信号量个数和最大信号量个数。当你WaitOne的时候,信号量自减,当Release的时候,信号量自增,然而当信号量为0的时候,后续的线程就不能拿到WaitOne了,所以必须等待先前的线程通过Release来释放。

    class Program{static void Main(string[] args){Thread t1 = new Thread(Run1);t1.Start();Thread t2 = new Thread(Run2);t2.Start();Thread t3 = new Thread(Run3);t3.Start();Console.ReadKey();}//初始可以授予2个线程信号,因为第3个要等待前面的Release才能得到信号static Semaphore sem = new Semaphore(2, 10);static void Run1(){sem.WaitOne();Console.WriteLine("大家好,我是Run1;" + DateTime.Now.ToString("mm:ss"));//两秒后Thread.Sleep(2000);sem.Release();}static void Run2(){sem.WaitOne();Console.WriteLine("大家好,我是Run2;" + DateTime.Now.ToString("mm:ss"));//两秒后Thread.Sleep(2000);sem.Release();}static void Run3(){sem.WaitOne();Console.WriteLine("大家好,我是Run3;" + DateTime.Now.ToString("mm:ss"));//两秒后Thread.Sleep(2000);sem.Release();}}

  输出:

  

  在以上的方法中Release()方法相当于自增一个信号量,Release(5)自增5个信号量。但是,Release()到构造函数的第二个参数maximumCount的值就不能再自增了。

  命名Semaphore可用于进程级交互。

    class Program{static void Main(string[] args){Thread t1 = new Thread(Run1);t1.Start();Thread t2 = new Thread(Run2);t2.Start();Console.Read();}//初始可以授予2个线程信号,因为第3个要等待前面的Release才能得到信号static Semaphore sem = new Semaphore(3, 10, "命名Semaphore");static void Run1(){sem.WaitOne();Console.WriteLine("进程:" +Process.GetCurrentProcess().Id + "  我是Run1" + DateTime.Now.TimeOfDay);}static void Run2(){sem.WaitOne();Console.WriteLine("进程:" + Process.GetCurrentProcess().Id + "  我是Run2" + DateTime.Now.TimeOfDay);}}

  输出如下:

  

  这个东西是跨进程的,如何测试,直接运行两次bin目录的exe文件,就能发现最多只能输出3个。

四、综合示例(线程、事件、信号量)

  要搞清楚线程、信号量、事件这三者的关系。实际上3个东西并无具体联系,各自有各自的作用,但是配合起来使用,威力无穷。

  下面用一个例子,结合事件、信号量、线程来实现如下功能:

  1. 主线程启动辅助线程执行一个长时间任务;
  2. 辅助线程完成时,触发完成事件(),调用委托,让主线程继续执行;
namespace ConsoleApplication3
{class Program{static AutoResetEvent ar = new AutoResetEvent(true);static void MyEventHandler(object sender, EventArgs e){ar.Set();}static void Main(string[] args){LongTimeWork LTW = new LongTimeWork();LTW.Completed += MyEventHandler;Thread t = new Thread(LTW.MyLongTimeWork);t.Start();//继续忙我的Thread.Sleep(2000);//等待辅助线程完成ar.WaitOne();Console.WriteLine("主线程完成!");Console.ReadKey();}}public class LongTimeWork{//定义一个事件public event EventHandler Completed;public void MyLongTimeWork(){Thread.Sleep(1000);Console.WriteLine("辅助线程长时间任务完成!");//当辅助线程完成时,触发已完成事件if (Completed != null){Completed(this, new EventArgs());}}}
}

  输出如下:

  

  以上虽然短短几十行代码,但是我却开发了两年多.Net之后才能够领悟。其主要作用是什么,以上达到了线程控制的目的,当我们开发一个核心模块时(LongTimeWork),仅仅暴露出一个事件(Completed),调用的人配合上信号量(AutoResetEvent),就能够随意调用你的核心模块。这也是WF4的调用方式。

转载于:https://www.cnblogs.com/Jeely/p/10750640.html

转载 信号量 第六篇相关推荐

  1. 秒杀多线程第十六篇 多线程十大经典案例之一 双线程读写队列数据

    本文配套程序下载地址为:http://download.csdn.net/detail/morewindows/5136035 转载请标明出处,原文地址:http://blog.csdn.net/mo ...

  2. SQL Server索引进阶第六篇:书签

    SQL Server索引进阶第六篇:书签 索引设计是数据库设计中比较重要的一个环节,对数据库的性能其中至关重要的作用,但是索引的设计却又不是那么容易的事情,性能也不是那么轻易就获取到的,很多的技术人员 ...

  3. objective-c 编程总结(第六篇)运行时操作 - 方法交换

    objective-c 编程总结(第六篇)运行时操作 - 方法交换 后面主要介绍oc类的运行时行为.这里面包括运行时方法的更换,消息的转发,以及动态属性.这些对于面向方面编程AOP的热爱者还是很有用的 ...

  4. 第六篇:python基础之文件处理

    第六篇:python基础之文件处理 阅读目录 一.文件处理流程 二.基本操作 2.1 文件操作基本流程初探 2.2 文件编码 2.3 文件打开模式 2.4 文件内置函数flush 2.5 文件内光标移 ...

  5. EnjoyingSoft之Mule ESB开发教程第六篇:Data Transform - 数据转换

    目录 1. 数据转换概念 2. 数据智能感知 - DataSense 3. 简单数据转换组件 3.1 Object to JSON 3.2 JSON to XML 3.3 JSON to Object ...

  6. Python开发【第六篇】:模块

    Python开发[第六篇]:模块 模块,用一砣代码实现了某个功能的代码集合. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一 ...

  7. 论文浅尝 | 六篇2020年知识图谱预训练论文综述

    转载公众号 | AI机器学习与知识图谱 本文介绍六篇有关知识图谱预训练的优秀论文,大致上可分为两类,生成学习模型和对比学习模型.其中GPT-GNN模型是生成学习模型,模型灵感来源于自然语言处理中的GP ...

  8. MVC教程第六篇:拦截器

    MVC教程第六篇:拦截器      摘要 本文将对"MVC公告发布系统"的发布公告功能添加日志功能和异常处理功能,借此来讨论ASP.NET MVC中拦截器的使用方法. 一个小难题 ...

  9. Python之路【第十六篇】:Django【基础篇】

    https://www.cnblogs.com/wupeiqi/articles/5237704.html Python之路[第十六篇]:Django[基础篇] Python的WEB框架有Django ...

  10. 年度最精彩研究,CVPR 2017六篇最佳论文介绍(附打包下载)| CVPR 2017

    雷锋网 AI 科技评论按:CVPR 2017的获奖论文已经在大会的第一天中公布,共有6篇论文获得四项荣誉.雷锋网 AI 科技评论对6篇获奖论文做了简要介绍如下. CVPR最佳论文 本届CVPR共有两篇 ...

最新文章

  1. scrapy和selenium结合抓取动态网页
  2. html应用缓存,HTML5应用缓存
  3. c语言中size of 用法,C语言中sizeof()的用法
  4. HR 的那些黑话大全,太扎心了!(漫画)
  5. RDIFramework.NET ━ 9.4 角色管理 ━ Web部分
  6. 前端学习(759):预解析案例
  7. Linux运维基础命令笔试题
  8. php 验证真实姓名,支付宝转账到支付宝 验证真实姓名
  9. 技术分析之OGNL表达式概述
  10. 目标检测——使用loss发现噪声数据
  11. vue指令和特殊特性
  12. 《零基础入门学习Python》学习过程笔记【40类和对象的相关内置函数】
  13. windows service 2012阿里云服务器在搭建mysql时缺少msvcr100.dll文件解决方案
  14. 3种结构ZnO基半导体纳米复合材料-图文详解
  15. mbr mysql_主引导记录MBR的结构和作用
  16. react navigation 中使用goBack()跳转到指定页面
  17. 遗传算法(Genetic Algorithm)解析
  18. java农夫过河_C语言实现农夫过河代码及解析
  19. c语言 取余 % 和除法 / 的应用技巧 (在取位数方面的)
  20. Qt 自定义(异形)形状按钮封装及实现点击弹跳效果

热门文章

  1. 短视频封面抽取和标题自动化生成
  2. 通用能力-智力题专项练习
  3. 大学生数学竞赛资料目录20190403更新
  4. quartus 中无法选择USB-Blaster下载程序的问题。
  5. 2019年天梯赛第一阶段(1-8)全解
  6. h5互动游戏制作方法是怎样_求h5游戏教程
  7. ad域下发策略_AD域修改组策略
  8. 梦幻手游登录显示服务器爆满,梦幻西游手游服务器爆满怎么办
  9. 华为nova5iotg功能使用_华为nova5有OTG功能吗?可以连接U盘和鼠标吗[多图]
  10. (八) 项目干系人管理