信号量 <第六篇>
一、ManualResetEvent
该对象有两种信号量状态True和False。构造函数设置初始状态。
- WaitOne:该方法用于阻塞线程,默认是无限期的阻塞,支持超时阻塞,如果超时就放弃阻塞,这样也就避免了无限期等待的尴尬;
- Set:手动修改信号量为True,也就是恢复线程执行;
- ReSet:重置状态;

class Program
{
public static void Main()
{
Thread t = new Thread(Run);
t.Name = "辅助线程";
t.Start(); Console.WriteLine("当前时间:{0} {1}准备执行!", DateTime.Now.TimeOfDay, t.Name);
//手动修改信号量为True,也就是恢复一个等待线程执行。
mr.Set(); Console.ReadKey();
} //一开始设置为false才会等待收到信号才执行
static ManualResetEvent mr = new ManualResetEvent(true); static void Run()
{
//线程开始执行时待命,收到信号才动身
mr.WaitOne();
Console.WriteLine("\n当前时间:{0} {1}正式执行!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); //我想让辅助线程暂停3秒
mr.WaitOne(3000);
Console.WriteLine("\n当前时间:{0} {1}暂停3秒,但是无效!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); //我想让辅助线程暂停
mr.Reset();
Console.WriteLine("\n当前时间:{0} {1}还是无效!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name);
}
}

输出如下:

Reset()的意思其实是重置,重置后才又能再WaitOne();

class Program
{
public static void Main()
{
Thread t = new Thread(Run);
t.Name = "辅助线程";
t.Start(); Console.WriteLine("当前时间:{0} {1}准备执行!", DateTime.Now.TimeOfDay, t.Name);
//手动修改信号量为True,也就是恢复一个等待线程执行。
mr.Set(); Thread.Sleep(10000);
mr.Set(); Console.ReadKey();
} //一开始设置为false才会等待收到信号才执行
static ManualResetEvent mr = new ManualResetEvent(false); static void Run()
{
//线程开始执行时待命,收到信号才动身
mr.WaitOne();
Console.WriteLine("\n当前时间:{0} {1}正式执行!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); //重置后停止才有效
mr.Reset();
//我想让辅助线程暂停3秒
mr.WaitOne(3000);
Console.WriteLine("\n当前时间:{0} {1}暂停3秒,这回有效了!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); //重置后停止才有效
mr.Reset();
//我想让辅助线程暂停,10后由主线程再次唤醒
mr.WaitOne();
Console.WriteLine("\n当前时间:{0} {1}暂停,但会被主线程再次唤醒,这回有效了!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name );
}
}

输出如下:

二、AutoResetEvent
AutoResetEvent与ManualResetEvent的区别在于AutoResetEvent 的WaitOne会改变信号量的值。
比如说初始信号量为True,如果WaitOne超时信号量将自动变为False,而ManualResetEvent则不会。

class Program
{
public static void Main()
{
Thread t = new Thread(Run);
t.Name = "辅助线程";
t.Start(); Console.WriteLine("当前时间:{0} {1}准备执行!", DateTime.Now.TimeOfDay, t.Name); Console.ReadKey();
} static AutoResetEvent ar = new AutoResetEvent(true); static void Run()
{
var state = ar.WaitOne(1000);
Console.WriteLine("当前的信号量状态:{0}", state); state = ar.WaitOne(1000);
Console.WriteLine("再次WaitOne后现在的状态是:{0}", state);
}
}

输出如下:

假如要实现上面ManualResetEvent同样的效果,Run方法就不用手动Reset()了:

static void Run()
{
//线程开始执行时待命,收到信号才动身
mr.WaitOne();
Console.WriteLine("\n当前时间:{0} {1}正式执行!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); //我想让辅助线程暂停3秒
mr.WaitOne(3000);
Console.WriteLine("\n当前时间:{0} {1}暂停3秒,这回有效了!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name); //我想让辅助线程暂停,10后由主线程再次唤醒
mr.WaitOne();
Console.WriteLine("\n当前时间:{0} {1}暂停,但会被主线程再次唤醒,这回有效了!", DateTime.Now.TimeOfDay, Thread.CurrentThread.Name);
}

少了手动Reset()代码。
三、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.Read();
} //初始可以授予2个线程信号,因为第3个要等待前面的Release才能得到信号
static Semaphore sem = new Semaphore(2, 10); static void Run1()
{
sem.WaitOne();
Console.WriteLine("大家好,我是Run1" + DateTime.Now.TimeOfDay);
} static void Run2()
{
sem.WaitOne();
Console.WriteLine("大家好,我是Run2" + DateTime.Now.TimeOfDay); //两秒后
Thread.Sleep(2000);
sem.Release();
} static void Run3()
{
sem.WaitOne();
Console.WriteLine("大家好,我是Run3" + DateTime.Now.TimeOfDay);
}
}

输出:

在以上的方法中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);
}
}

输出如下:

- ManualResetEvent:每次可以唤醒一个或多个线程;
- AutoResetEvent:每次只能唤醒一个线程;
信号量 <第六篇>的更多相关文章
- 转载 信号量 <第六篇>
一.ManualResetEvent 该对象有两种信号量状态True和False.构造函数设置初始状态.简单来说, 如果构造函数由true创建,则第一次WaitOne()不会阻止线程的执行,而是等待R ...
- 解剖SQLSERVER 第十六篇 OrcaMDF RawDatabase --MDF文件的瑞士军刀(译)
解剖SQLSERVER 第十六篇 OrcaMDF RawDatabase --MDF文件的瑞士军刀(译) http://improve.dk/orcamdf-rawdatabase-a-swiss-a ...
- 解剖SQLSERVER 第六篇 对OrcaMDF的系统测试里避免regressions(译)
解剖SQLSERVER 第六篇 对OrcaMDF的系统测试里避免regressions (译) http://improve.dk/avoiding-regressions-in-orcamdf-b ...
- Python之路【第十六篇】:Django【基础篇】
Python之路[第十六篇]:Django[基础篇] Python的WEB框架有Django.Tornado.Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了O ...
- 第六篇 :微信公众平台开发实战Java版之如何自定义微信公众号菜单
我们来了解一下 自定义菜单创建接口: http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_to ...
- RabbitMQ学习总结 第六篇:Topic类型的exchange
目录 RabbitMQ学习总结 第一篇:理论篇 RabbitMQ学习总结 第二篇:快速入门HelloWorld RabbitMQ学习总结 第三篇:工作队列Work Queue RabbitMQ学习总结 ...
- 第六篇 Replication:合并复制-发布
本篇文章是SQL Server Replication系列的第六篇,详细内容请参考原文. 合并复制,类似于事务复制,包括一个发布服务器,一个分发服务器和一个或多个订阅服务器.每一个发布服务器上可以定义 ...
- 第六篇 Integration Services:初级工作流管理
本篇文章是Integration Services系列的第六篇,详细内容请参考原文. 简介在前几篇文章中,我们关注使用增量加载方式加载数据.在本篇文章,我们将关注使用优先约束管理SSIS控制流中的工作 ...
- 第六篇 SQL Server安全执行上下文和代码签名
本篇文章是SQL Server安全系列的第六篇,详细内容请参考原文. SQL Server决定主体是否有必要的执行代码权限的根本途径是其执行上下文规则.这一切都可能复杂一个主体有执行代码的权限,但是却 ...
随机推荐
- 新唐的icp和isp
ICP 的意思是 "在电路编程"(in Circuit Programming),PC 电脑可以运行 “NuMicro ICP Programming Tool”软件通过 Nu-L ...
- Send竞争对手:百度云一小时,QQ超大附件最多支持2G,邮件附件20M到50M不等(附国外所有storage列表)——痛点是,最大传输2G,最大容量只有3G(和微云不是一回事),转存到微云文件不能超过1G
QQ邮箱最大可发送50M普通附件(群邮件则限制在2M).此外也可以使用超大附件功能,支持将1G的文件发往任意邮箱.QQ邮箱根据你的QQ邮箱容量的不同制定相应的接受附件限制,包括附件在内,2G用户所发送 ...
- AQS详解
一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronized(AQS)! 类如其名,抽象的队列式的同步器,AQ ...
- (\S)? 匹配0个或者一个前导字符
---------------------------------------------- "1" 模式: \"(?<id>(\S+)?)\" ? ...
- C# yield return 流程理解
代码如下: 在Documents1方法中使用yield return之后, 下次在进入Documents1方法就是从上一次yield return部分执行 using System; using S ...
- Unity 屏幕适配小脚本
屏幕适配是可以通过代码实现的,相信给你时间就一定能写出来. 我们公司貌似没有分辨率适配框架通常对应小屏幕的苹果4要额外设置下等等就完了! 屏幕适配框架实现思路: 通过代码获取当前的分辨率 –> ...
- Windows Live Writer 代码插件改造
源码和插件都在后面,如果不想看我神神叨叨的可以直接到文章后面下载 一 .找插件 在使用Windows Live Writer 经常要用到插入代码的功能,根据博客园中教程,分别使用了: WindowsL ...
- 【Oracle】逻辑结构(TableSpace→Segment→Extent→Block)
一.逻辑体系结构图 二.逻辑结构图组成介绍 从上表能够看出,一个数据库是由多个表空间(tablespace)组成,一个表空间又由多个段(segment)组成,一个段又由多个区(extent)组成 ...
- POJ 1469 ZOJ1140 二分匹配裸题
很裸,左点阵n,右点阵m 问最大匹配是否为n #include <cstdio> #include <cstring> #include <vector> usin ...
- [Immutable.js] Working with Subsets of an Immutable.js Map()
Immutable.js offers methods to break immutable structures into subsets much like Array--for instance ...