.NET 同步与异步 之 EventWaitHandle(Event通知) (十三)
本随笔续接:.NET 同步与异步 之 Mutex (十二)
在前一篇我们已经提到过Mutex和本篇的主角们直接或间接继承自 WaitHandle:
- Mutex类,这个我们在上一篇已经讲过。
- EventWaitHandle 类及其派生类AutoResetEvent 和 ManualResetEvent,这是本篇的主角。
- Semaphore 类,即信号量,我们下一篇再讲(忽然觉得没有必要介绍了)。
WaitHandle提供了若干用于同步的方法。上一篇关于Mutex的blog中已经讲到一个WaitOne(),这是一个实例方法。除此之外,WaitHandle另有3个用于同步的静态方法:
- SignalAndWait(WaitHandle, WaitHandle):以原子操作的形式,向第一个WaitHandle发出信号并等待第二个。即唤醒阻塞在第一个WaitHandle上的线程/进程,然后自己等待第二个WaitHandle,且这两个动作是原子性的。跟WaitOne()一样,这个方法另有两个重载方法,分别用Int32或者TimeSpan来定义等待超时时间,以及是否从上下文的同步域中退出。
- WaitAll(WaitHandle[]):这是用于等待WaitHandle数组里的所有成员。如果一项工作,需要等待前面所有人完成才能继续,那么这个方法就是一个很好的选择。仍然有两个用于控制等待超时的重载方法,请自行参阅。
- WaitAny(WaitHandle[]):与WaitAll()不同,WaitAny只要等到数组中一个成员收到信号就会返回。如果一项工作,你只要等最快做完的那个完成就可以开始,那么WaitAny()就是你所需要的。它同样有两个用于控制等待超时的重载。
线程相关性
- Mutex与Monitor一样,是具有线程相关性的。我们之前已经提到过,只有通过Monitor.Enter()/TryEnter()获得对象锁的线程才能调用Pulse()/Wait()/Exit();同样的,只有获得Mutex拥有权的线程才能执行ReleaseMutex()方法,否则就会引发异常。这就是所谓的线程相关性。
- 相反,EventWaitHandle以及它的派生类AutoResetEvent和ManualResetEvent都是线程无关的。任何线程都可以发信号给EventWaitHandle,以唤醒阻塞在上面的线程。
- 下一篇要提到的Semaphore也是线程无关的。
Event通知
EventWaitHandle、AutoResetEvent、ManualResetEvent名字里都有一个“Event”,不过这跟.net的本身的事件机制完全没有关系,它不涉及任何委托或事件处理程序。相对于我们之前碰到的Monitor和Mutex需要线程去争夺“锁”而言,我们可以把它们理解为一些需要线程等待的“事件”。线程通过等待这些事件的“发生”,把自己阻塞起来。一旦“事件”完成,被阻塞的线程在收到信号后就可以继续工作。
为了配合WaitHandle上的3个静态方法SingnalAndWait()/WailAny()/WaitAll(),EventWaitHandle提供了自己独有的,使“Event”完成和重新开始的方法:
- bool:Set():英文版MSDN:Sets the state of the event to signaled, allowing one or more waiting threads to proceed;中文版MSDN:将事件状态设置为终止状态,允许一个或多个等待线程继续。初看“signaled”和“终止”似乎并不对应,细想起来这两者的说法其实也不矛盾。事件如果在进行中,当然就没有“终止”,那么其它线程就需要等待;一旦事件完成,那么事件就“终止”了,于是我们发送信号唤醒等待的线程,所以“信号已发送”状态也是合理的。两个小细节:
- 无论中文还是英文版,都提到这个方法都是可以让“一个”或“多个”等待线程“继续/Proceed”(注意不是“唤醒”)。所以这个方法在“唤醒”这个动作上是类似于Monitor.Pulse()和Monitor.PulseAll()的。至于什么时候类似Pulse(),又在什么时候类似PulseAll(),往下看。
- 这个方法有bool型的返回值:如果该操作成功,则为true;否则,为false。不过MSDN并没有告诉我们,什么时候执行会失败,你只有找个微软MVP问问了。
- bool:Reset():Sets the state of the event to nonsignaled, causing threads to block. 将事件状态设置为非终止状态,导致线程阻止。 同样,我们需要明白“nonsignaled”和“非终止”是一回事情。还同样的是,仍然有个无厘头的返回值。Reset()的作用,相当于让事件重新开始处于“进行中”,那么此后所有WaitOne()/WaitAll()/WaitAny()/SignalAndWait()这个事件的线程都会再次被挡在门外。
构造函数
来看看EventWaitHandle众多构造函数中最简单的一个:
- EventWaitHandle(Boolean initialState, EventResetMode mode):初始化EventWaitHandle类的新实例,并指定等待句柄最初是否处于终止状态,以及它是自动重置还是手动重置。大多数时候我们会在第一个参数里使用false,这样新实例会缺省为“非终止”状态。第二个参数EventResetMode是一个枚举,一共两个值:
- EventResetMode.AutoReset:当Set()被调用当前EventWaitHandle转入终止状态时,若有线程阻塞在当前EventWaitHandle上,那么在释放一个线程后EventWaitHandle就会自动重置(相当于自动调用Reset())再次转入非终止状态,剩余的原来阻塞的线程(如果有的话)还会继续阻塞。如果调用Set()后本没有线程阻塞,那么EventWaitHandle将保持“终止”状态直到一个线程尝试等待该事件,这个该线程不会被阻塞,此后EventWaitHandle才会自动重置并阻塞那之后的所有线程。
- EventResetMode.ManualReset:当终止时,EventWaitHandle 释放所有等待的线程,并在手动重置前,即Reset()被调用前,一直保持终止状态。
好了,现在我们可以清楚的知道Set()在什么时候分别类似于Monitor.Pulse()/PulseAll()了:
- 当EventWaitHandle工作在AutoReset模式下,就唤醒功能而言,Set()与Monitor.Pulse()类似。此时,Set()只能唤醒众多(如果有多个的话)被阻塞线程中的一个。但两者仍有些差别:
- Set()的作用不仅仅是“唤醒”而是“释放”,可以让线程继续工作(proceed);相反,Pulse()唤醒的线程只是重新进入Running状态,参与对象锁的争夺,谁都不能保证它一定会获得对象锁。
- Pulse()的已被调用的状态不会被维护。因此,如果在没有等待线程时调用Pulse(),那么下一个调用Monitor.Wait()的线程仍然会被阻塞,就像Pulse() 没有被被调用过。也就是说Monitor.Pulse()只在调用当时发挥作用,并不象Set()的作用会持续到下一个WaitXXX()。
- 在一个工作在ManualReset模式下的EventWaitHandle的Set()方法被调用时,它所起到的唤醒作用与Monitor.PulseAll()类似,所有被阻塞的线程都会收到信号被唤醒。而两者的差别与上面完全相同。
来看看EventWaitHandle的其它构造函数:
- EventWaitHandle(Boolean initialState, EventResetMode mode, String name):头两个参数我们已经看过,第三个参数name用于在系统范围内指定同步事件的名称。是的,正如我们在Mutex一篇中提到的,由于父类WaitHandle是具有跨进程域的能力的,因此跟Mutex一样,我们可以创建一个全局的EventWaitHandle,让后将它用于进程间的通知。注意,name仍然是大小写敏感的,仍然有命名前缀的问题跟,你可以参照这里。当name为null或空字符串时,这等效于创建一个局部的未命名的EventWaitHandle。仍然同样的还有,可能会因为已经系统中已经有同名的EventWaitHandle而仅仅返回一个实例表示同名的EventWaitHandle。所以最后仍旧同样地,如果你需要知道这个EventWaitHandle是否由你最先创建,你需要使用以下两个构造函数之一。
- EventWaitHandle(Boolean initialState, EventResetMode mode, String name, out Boolean createdNew):createdNew用于表明是否成功创建了EventWaitHandle,true表明成功,false表明已经存在同名的事件。
- EventWaitHandle(Boolean initialState, EventResetMode mode, String name, out Boolean createdNew, EventWaitHandleSecurity):关于安全的问题,直接查看这个构造函数上的例子吧。全局MutexEventWaitHandle的安全问题应该相对Mutex更需要注意,因为有可能黑客程序用相同的事件名对你的线程发送信号或者进行组织,那样可能会严重危害你的业务逻辑。
MSDN Demo
using System;
using System.Threading; public class Example
{
// The EventWaitHandle used to demonstrate the difference
// between AutoReset and ManualReset synchronization events.
//
private static EventWaitHandle ewh; // A counter to make sure all threads are started and
// blocked before any are released. A Long is used to show
// the use of the 64-bit Interlocked methods.
//
private static long threadCount = ; // An AutoReset event that allows the main thread to block
// until an exiting thread has decremented the count.
//
private static EventWaitHandle clearCount =
new EventWaitHandle(false, EventResetMode.AutoReset); [MTAThread]
public static void Main()
{
// Create an AutoReset EventWaitHandle.
//
ewh = new EventWaitHandle(false, EventResetMode.AutoReset); // Create and start five numbered threads. Use the
// ParameterizedThreadStart delegate, so the thread
// number can be passed as an argument to the Start
// method.
for (int i = ; i <= ; i++)
{
Thread t = new Thread(
new ParameterizedThreadStart(ThreadProc)
);
t.Start(i);
} // Wait until all the threads have started and blocked.
// When multiple threads use a 64-bit value on a 32-bit
// system, you must access the value through the
// Interlocked class to guarantee thread safety.
//
while (Interlocked.Read(ref threadCount) < )
{
Thread.Sleep();
} // Release one thread each time the user presses ENTER,
// until all threads have been released.
//
while (Interlocked.Read(ref threadCount) > )
{
Console.WriteLine("Press ENTER to release a waiting thread.");
Console.ReadLine(); // SignalAndWait signals the EventWaitHandle, which
// releases exactly one thread before resetting,
// because it was created with AutoReset mode.
// SignalAndWait then blocks on clearCount, to
// allow the signaled thread to decrement the count
// before looping again.
//
WaitHandle.SignalAndWait(ewh, clearCount);
}
Console.WriteLine(); // Create a ManualReset EventWaitHandle.
//
ewh = new EventWaitHandle(false, EventResetMode.ManualReset); // Create and start five more numbered threads.
//
for(int i=; i<=; i++)
{
Thread t = new Thread(
new ParameterizedThreadStart(ThreadProc)
);
t.Start(i);
} // Wait until all the threads have started and blocked.
//
while (Interlocked.Read(ref threadCount) < )
{
Thread.Sleep();
} // Because the EventWaitHandle was created with
// ManualReset mode, signaling it releases all the
// waiting threads.
//
Console.WriteLine("Press ENTER to release the waiting threads.");
Console.ReadLine();
ewh.Set(); } public static void ThreadProc(object data)
{
int index = (int) data; Console.WriteLine("Thread {0} blocks.", data);
// Increment the count of blocked threads.
Interlocked.Increment(ref threadCount); // Wait on the EventWaitHandle.
ewh.WaitOne(); Console.WriteLine("Thread {0} exits.", data);
// Decrement the count of blocked threads.
Interlocked.Decrement(ref threadCount); // After signaling ewh, the main thread blocks on
// clearCount until the signaled thread has
// decremented the count. Signal it now.
//
clearCount.Set();
}
}
Demo
附,Demo : http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip
参见更多:随笔导读:同步与异步
(该系列随笔暂告一段落)
.NET 同步与异步 之 EventWaitHandle(Event通知) (十三)的更多相关文章
- 同步与异步&阻塞与非阻塞
摘要 一直为同步异步,阻塞非阻塞概念所困扰,特定总结了下,原来是这么个意思 一直为同步异步,阻塞非阻塞概念所困扰,特定总结了下 一.同步与异步的区别 1.概念介绍 同步:所谓同步是一个服务的完成需要依 ...
- JavaScript:彻底理解同步、异步和事件循环(Event Loop) (转)
原文出处:https://segmentfault.com/a/1190000004322358 一. 单线程 我们常说"JavaScript是单线程的". 所谓单线程,是指在JS ...
- JavaScript:彻底理解同步、异步和事件循环(Event Loop)
一. 单线程 我们常说"JavaScript是单线程的". 所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个.不妨叫它主线程. 但是实际上还存在其他 ...
- [转] JavaScript:彻底理解同步、异步和事件循环(Event Loop)
一. 单线程 我们常说“JavaScript是单线程的”. 所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个.不妨叫它主线程. 但是实际上还存在其他的线程.例如:处理A ...
- .NET 同步与异步 之 Mutex (十二)
本随笔续接:.NET 同步与异步 之 线程安全的集合 (十一) 本随笔 及 接下来的两篇随笔,将介绍 .NET 同步与异步系列 的最后一个大块知识点:WaitHandle家族. 抽象基类:WaitHa ...
- python学习笔记-(十四)I/O多路复用 阻塞、非阻塞、同步、异步
1. 概念说明 1.1 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系统的核心是内核,独立于普通的应用程序,可 ...
- 关于js中的同步和异步
最近看到前端面试问到js中的同步和异步,这个问题该怎么回答? 梳理一下,js对于异步的处理,很多人的第一反应是ajax,这只能说是对了一半. 1.个人觉得,js中,最基础的异步是setTimeout和 ...
- IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇)
IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇) 当你发现自己最受欢迎的一篇blog其实大错特错时,这绝对不是一件让人愉悦的事. <IO - 同步,异步,阻塞,非阻塞 >是我在开始学习e ...
- 半同步半异步模式的实现 - MSMQ实现
半同步半异步模式的实现 - MSMQ实现 所谓半同步半异步是指,在某个方法调用中,有些代码行是同步执行方式,有些代码行是异步执行方式,下面我们来举个例子,还是以经典的PlaceOrder来说,哈哈. ...
随机推荐
- JAVA项目中常用的异常处理情况
1.数学运算异常( java.lang.arithmeticexception) 程序中出现了除以零这样的运算就会出这样的异常,对这种异常,大家就要好好检查一下自己程序中涉及到数学运算的地方,公式是不 ...
- 【JavaScript】浏览器
No1: [window]全局作用域,而且表示浏览器窗口 innerWidth和innerHeight属性,可以获取浏览器窗口的内部宽度和高度.内部宽高是指除去菜单栏.工具栏.边框等占位元素后,用于显 ...
- Codeforces 514C Watto and Mechanism 【Trie树】+【DFS】
<题目链接> 题目大意:输入n个单词构成单词库,然后进行m次查询,每次查询输入一个单词(注意这些单词只由a,b,c构成),问该单词库中是否存在与当前查询的单词有且仅有一个字符不同的单词. ...
- HDU 1540 Tunnel Warfare(经典)(区间合并)【线段树】
<题目链接> 题目大意: 一个长度为n的线段,下面m个操作 D x 表示将单元x毁掉 R 表示修复最后毁坏的那个单元 Q x 询问这个单元以及它周围有多少个连续的单元,如果它本身已经被 ...
- POJ_2376_Cleaning Shifts【贪心】【区间覆盖】
题目链接 题目大意: 有一些奶牛,每只奶牛负责一个时间段.问覆盖完全部的时间段最少需要多少只奶牛.若不能全部覆盖,输出-1. #include <cstdio>#include <a ...
- 从零搭建 ES 搜索服务(三)同义词搜索
一.前言 上篇介绍了 ES 的基础搜索,能满足我们基本的需求,然而在实际使用中还可能希望搜索「番茄」能将包含「西红柿」的结果也罗列出来,本篇将介绍如何实现同义词之间的搜索. 二.安装 ES 同义词插件 ...
- Linux系统开发之路-中
4.Linux的安装(Windows环境下): 1)Windows环境需要借助虚拟机来安装Linux系统,这个推荐使用的软件是VMWare,官网能下载到的最新版本是Workstation Pro15. ...
- sql - 递归update
declare v_rlt ):; l_sql ); -- variable that contains a query l_c sys_refcursor; -- cursor variable(w ...
- Farewell Party-构造
Farewell Party 思路 : 转换思路 ,有 a [ i ] 个不相等的 ,那么至少得有 n - a [ i ]个与它相等的. 但是有可能与它拥有相同数目的有很多. 但是为了能够最终 分配成 ...
- BZOJ.2653.[国家集训队]middle(可持久化线段树 二分)
BZOJ 洛谷 求中位数除了\(sort\)还有什么方法?二分一个数\(x\),把\(<x\)的数全设成\(-1\),\(\geq x\)的数设成\(1\),判断序列和是否非负. 对于询问\(( ...