多线程EventWaitHandle -戈多编程
在.NET的System.Threading命名空间中有一个名叫WaitHandler的类,这是一个抽象类(abstract),我们无法手动去创建它,但是WaitHandler有三个子类,这三个子类分别是:System.Threading.EventWaitHandle,System.Threading.Mutex,System.Threading.Semaphore,这三个类都是非Abstract的,可以由开发者来创建和使用,其中本文主要讨论的是其中的System.Threading.EventWaitHandle类。
EventWaitHandle类的用途是可以调用其WaitOne方法来阻塞线程的运行,直到得到一个信号(该信号由EventWaitHandle类的Set方法发出),然后释放线程让其不再阻塞继续运行。
EventWaitHandle类拥有两种状态,终止状态 和 非终止状态:
- 在终止状态下,被WaitOne阻塞的线程会逐个得到释放,所以当EventWaitHandle始终处于终止状态时,调用其WaitOne方法无法起到阻塞线程的作用,因为线程被其WaitOne方法阻塞后,会立即被释放掉。
- 在非终止状态下,被WaitOne阻塞的线程会继续被阻塞,如果一个线程在EventWaitHandle对象处于非终止状态时调用了其WaitOne函数,该线程会立即被阻塞。
需要注意的是终止状态和非终止状态之间,是可以相互转换的。调用EventWaitHandle对象的Set方法既可以将EventWaitHandle对象设置为终止状态,调用EventWaitHandle对象的Reset方法既可以将EventWaitHandle对象设置为非终止状态。
此外,EventWaitHandle类还拥有两种模式,AutoReset 和 ManualReset 模式:
- 在AutoReset模式下,当EventWaitHandle对象被置为终止状态时,释放一个被WaitOne阻塞的线程后,EventWaitHandle对象会马上被设置为非终止状态,这个过程就等同于一个被WaitOne阻塞的线程被释放后,自动调用了EventWaitHandle的Reset方法,将EventWaitHandle对象自动从终止状态置回了非终止状态,所以这种模式叫AutoReset模式。所以如果有若干线程被EventWaitHandle对象的WaitOne方法阻塞了,每调用一次EventWaitHandle对象的Set方法将EventWaitHandle对象置为终止状态后,只能释放一个被阻塞的线程,然后EventWaitHandle对象又会被置为非终止状态。如果EventWaitHandle对象的Set方法之后又被调用了一次,剩下那些被阻塞的线程中,又会有一个线程被释放。所以如果有8个被WaitOne方法阻塞的线程,那么需要调用次EventWaitHandle对象的Set方法8次,才能让所有线程都得到释放。需要注意的一点就是MSDN中有提到:如果两次EventWaitHandle对象的Set方法调用非常接近,以至于当第一次调用Set方法后,被阻塞的线程还没有来得及释放,第二次Set调用又开始了,那么这两次Set方法的调用只会让一个被阻塞的线程被释放,也就是说如果两次Set方法的调用过于接近,那么就相当于只调用了一次。原因就是因为由于两次Set调用过于接近,当第一次Set调用后,其释放的线程还没有完全被释放,即EventWaitHandle对象还没有被置回非终止状态,第二次Set调用又开始了,又要求EventWaitHandle对象变成终止状态去释放剩余的阻塞线程,但是问题是现在EventWaitHandle对象本来就处于终止状态,并且第一次Set调用后的那个被释放的线程还没有被完全释放,所以现在不能去释放剩余的阻塞线程。之后待第一次Set调用后的那个被释放线程完全释放后,由于EventWaitHandle对象处于AutoReset模式,所以现在EventWaitHandle对象才会被置回非终止状态,那么就相当于第二次Set调用就白白浪费了一次机会去将EventWaitHandle对象置为终止状态去释放剩余的阻塞线程。
- 在ManualReset模式下,当EventWaitHandle对象被置为终止状态时,释放一个被WaitOne阻塞的线程后,其状态不会改变,仍然处于终止状态,所以当ManualReset模式下EventWaitHandle对象处于终止状态时,会连续释放所有被WaitOne方法阻塞的线程,直到手动调用其Reset方法将其置回非终止状态。所以这种模式叫ManualReset模式。
使用EventWaitHandle类的构造函数可以设置EventWaitHandle对象的模式和初始状态,以下是EventWaitHandle类的其中一个构造函数:
public EventWaitHandle
(
bool initialState,
EventResetMode mode
)
- 第一个参数initialState为true时,表示EventWaitHandle对象的初始状态为终止状态,反之false表示EventWaitHandle对象的初始状态为非终止状态。
- 第二个参数EventResetMode mode为EventResetMode.AutoReset时,表示EventWaitHandle对象处于AutoReset模式,当第二个参数为EventResetMode.ManualReset时,表示EventWaitHandle对象处于ManualReset模式。
需要注意的是,当EventWaitHandle对象的初始状态为终止状态,模式为AutoReset时,第一个被EventWaitHandle对象的WaitOne方法阻塞的线程会立即被释放,然后EventWaitHandle对象被置为非终止状态,如果后面还有线程被EventWaitHandle对象的WaitOne方法阻塞,就只有等到EventWaitHandle对象的Set方法调用,才能被释放了。原因我就不多说了,如果你还不明白请回去看上面对EventWaitHandle的终止状态和AutoReset模式的阐述。
下面是EventWaitHandle类的一个综合示例:
using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace EventWaitHandleDemo
{
class Program
{
static EventWaitHandle eHandle; static void UnblockDemo()
{
Console.WriteLine("测试EventWaitHandle的初始终止状态");
eHandle = new EventWaitHandle(true, EventResetMode.AutoReset);//eHandle初始为终止状态,模式为AutoReset
eHandle.WaitOne();//由于EventWaitHandle对象eHandle初始状态为终止状态,所以这里第一次调用WaitOne时阻塞被立即释放,又由于eHandle为AutoReset模式,所以之后eHandle会被置为非终止状态
Console.WriteLine("线程未被阻塞");
eHandle.WaitOne();//由于此时eHandle已经为非终止状态,所以此时调用WaitOne线程会被阻塞
Console.WriteLine("线程被阻塞");
} static void BlockDemo()
{
Console.WriteLine("测试EventWaitHandle的初始非终止状态");
eHandle = new EventWaitHandle(false, EventResetMode.AutoReset);//eHandle初始为非终止状态,模式为AutoReset
eHandle.WaitOne();//由于EventWaitHandle对象eHandle初始状态为非终止状态,所以这里第一次调用WaitOne时,线程就被组塞了
Console.WriteLine("线程被阻塞");
} static void AutoResetDemo()
{
Console.WriteLine("测试EventWaitHandle的AutoReset模式");
eHandle = new EventWaitHandle(false, EventResetMode.AutoReset);//eHandle初始为非终止状态,模式为AutoReset
ThreadPool.QueueUserWorkItem(new WaitCallback((object o) =>
{
//启动另一个线程,每隔3秒钟调用一次eHandle.Set方法,为主线程释放一次阻塞,一共释放3次
for (int i = ; i < ; i++)
{
Thread.Sleep();
eHandle.Set();//由于eHandle处于AutoReset模式,所以每次使用Set将eHandle置为终止状态后,待被WaitOne阻塞的线程被释放后,eHandle又会被自动置回非终止状态
}
}), null); eHandle.WaitOne();//线程第一次被WaitOne阻塞
Console.WriteLine("第一次WaitOne调用阻塞已被释放,3秒后第二次WaitOne调用的阻塞会被释放");
eHandle.WaitOne();//线程第二次被WaitOne阻塞
Console.WriteLine("第二次WaitOne调用阻塞已被释放,3秒后第三次WaitOne调用的阻塞会被释放");
eHandle.WaitOne();//线程第三次被WaitOne阻塞
Console.WriteLine("第三次WaitOne调用阻塞已被释放,所有WaitOne调用的阻塞都已被释放");
} static void ManualResetDemo()
{
Console.WriteLine("测试EventWaitHandle的ManualReset模式");
eHandle = new EventWaitHandle(false, EventResetMode.ManualReset);//eHandle初始为非终止状态,模式为ManualReset
ThreadPool.QueueUserWorkItem(new WaitCallback((object o) =>
{
//启动另一线程,3秒后调用一次eHandle.Set方法,为主线程释放WaitOne阻塞
Thread.Sleep();
eHandle.Set();//由于eHandle处于ManualReset模式,所以一旦使用Set将eHandle置为终止状态后,在eHandle的Reset被调用前eHandle会一直处于终止状态,在eHandle调用Reset前,所有被WaitOne阻塞的线程会立即得到释放
}), null); eHandle.WaitOne();//线程第一次被WaitOne阻塞
Console.WriteLine("第一次WaitOne调用阻塞已被释放,第二次WaitOne调用的阻塞会被立即释放");
eHandle.WaitOne();//线程第二次被WaitOne阻塞
Console.WriteLine("第二次WaitOne调用阻塞已被释放,第三次WaitOne调用的阻塞会被立即释放");
eHandle.WaitOne();//线程第三次被WaitOne阻塞
Console.WriteLine("第三次WaitOne调用阻塞已被释放,所有WaitOne调用的阻塞都已被释放"); eHandle.Reset();//调用eHandle的Reset方法,将eHandle手动置回非终止状态,之后再调用WaitOne方法就会被阻塞了
eHandle.WaitOne();//线程第四次被WaitOne阻塞
Console.WriteLine("第四次WaitOne调用阻塞已被释放");
} static void Main(string[] args)
{
Console.Write("你想测试哪一个方法1=UnblockDemo,2=BlockDemo,3=AutoResetDemo,4=ManualResetDemo:");
switch (Console.ReadLine())
{
case "":
UnblockDemo();
break;
case "":
BlockDemo();
break;
case "":
AutoResetDemo();
break;
case "":
ManualResetDemo();
break;
default:
break;
}
}
}
}
最后要提一下,那就是EventWaitHandle类还有两个子类: System.Threading.AutoResetEvent类 和 System.Threading.ManualResetEvent类:
AutoResetEvent类和EventWaitHandle类处于AutoReset模式时类似
ManualResetEvent类和EventWaitHandle类处于ManualReset模式时类似
这两个类和EventWaitHandle类的用法几乎相同,所以这里就不多说了。
多线程EventWaitHandle -戈多编程的更多相关文章
- iOS多线程拾贝------操作巨人编程
iOS多线程拾贝------操作巨人编程 多线程 基本 实现方案:pthread - NSThread - GCD - NSOperation Pthread 多平台,可移植 c语言,要程序员管理生命 ...
- 深入理解C#多线程 -戈多编程
引用(http://www.cnblogs.com/luxiaoxun/p/3280146.html) 一.使用线程的好处 1.可以使用线程将代码同其他代码隔离,提高应用程序的可靠性. 2.可以使 ...
- I/O系统,多线程、图形用户界面编程
多线程 进程与线程区别: 进程需要分配独立的内存空间:线程在同一内存空间中工作,可以共享同一块内存和系统资源 与Java相关的API: 1)Thread类 方法:start()启动: urn() : ...
- C++程序员面试题目总结(涉及C++基础、多线程多进程、网络编程、数据结构与算法)
说明:C++程序员面试题目总结(涉及C++基础知识.多线程多进程.TCP/IP网络编程.Linux操作.数据结构与算法) 内容来自作者看过的帖子或者看过的文章,个人整理自互联网,如有侵权,请联系作者 ...
- IO复用、多进程和多线程三种并发编程模型
I/O复用模型 I/O复用原理:让应用程序可以同时对多个I/O端口进行监控以判断其上的操作是否可以进行,达到时间复用的目的.在书上看到一个例子来解释I/O的原理,我觉得很形象,如果用监控来自10根不同 ...
- Delphi多线程下的ADO编程
前言: 几个月前接到一个任务:将一后台程序访问数据库的方式从BDE改为ADO,原因是由于业务量的增加,通过BDE不论是向数据库写入数据还是从数据库中读出数据的速度都变得无法忍受,大家都知道ADO在数据 ...
- 【多线程】无锁编程以及CAS
无锁编程 / lock-free / 非阻塞同步 无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Sy ...
- 【多线程】Java并发编程:Lock(转载)
原文链接:http://www.cnblogs.com/dolphin0520/p/3923167.html Java并发编程:Lock 在上一篇文章中我们讲到了如何使用关键字synchronized ...
- 【多线程】Java并发编程:并发容器之CopyOnWriteArrayList(转载)
原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容 ...
随机推荐
- ASP.NET Core 2.2 : 二十. Action的多数据返回格式处理机制
上一章讲了系统如何将客户端提交的请求数据格式化处理成我们想要的格式并绑定到对应的参数,本章讲一下它的“逆过程”,如何将请求结果按照客户端想要的格式返回去. 一.常见的返回类型 以系统模板默认生成的Ho ...
- Empire – PowerShell 后渗透攻击框架
0x01 简介 Empire是一个后开发框架.它是一个纯粹的PowerShell代理,具有加密安全通信和灵活架构的附加功能.Empire具有在不需要PowerShell.exe的情况下执行PowerS ...
- UI自动化测试养成记
<selenium自动化测试实战>PDF文档下载:https://pan.baidu.com/s/16dt8qPi-C4BOgKe6snAA0A 这几个月我都干了些什么? 当我打算写一本& ...
- 致初学者(三): HDU 2033~ 2043题解
下面继续给出HDU 2033~2043的AC程序,供大家参考.2033~2043这10道题就被归结为“ACM程序设计期末考试(2006/06/07) ”和“2005实验班短学期考试 ”. HDU 20 ...
- GlusterFs卷类型分析及创建、使用(结合kubernetes集群分析)
引言 本文通过对卷类型的分析对比,来帮助读者选取生产环境最符合服务的挂载存储,命令可结合<glusterfs详解及kubernetes 搭建heketi-glusterfs>进行实验,下面 ...
- 松软科技课堂:SQL之NOTNULL约束
SQL NOT NULL 约束 NOT NULL 约束强制列不接受 NULL 值. NOT NULL 约束强制字段始终包含值.这意味着,如果不向字段添加值,就无法插入新记录或者更新记录. 下面的 SQ ...
- 让Samba支持Windows10的自动发现
Windows10如果开了SMB 1.0支持,就非常不安全,不开就搜索不到samba的NETBIOS. 在安装配置好samba,并且确认windows可以通过netbios名访问后. 可以使用http ...
- .NET分布式大规模计算利器-Orleans(一)
写在前面 Orleans是基于Actor模型思想的.NET领域的框架,它提供了一种直接而简单的方法来构建分布式大规模计算应用程序,而无需学习和应用复杂的并发或其他扩展模式.我在2015年下半年开始 ...
- Django模板语言 标签整理
Django模板语言 标签 内置标签引用 1. autoescape 控制自动转义是否可用. 这种标签带有任何 on 或 off 作为参数的话,他将决定转义块内效果. 该标签会以一个endautoes ...
- ajax发送PUT请求,使用HttpPutFormContentFilter过滤器接受办法
相信在使用ajax发送put请求时候,肯定遇到过后端数据无法被接受到的405错误. 为什么会遇到这个问题? 1.首先查看Tomcat源码 关于如何将数据封装到Request public class ...