上一篇介绍了经过lock关键字和Monitor类型进行线程同步,本篇中就介绍一下经过同步句柄进行线程同步。数组
在Windows系统中,可使用内核对象进行线程同步,内核对象由系统建立并维护。内核对象为内核所拥有,因此不一样进程能够访问同一个内核对象, 如进程、线程、事件、信号量、互斥量等都是内核对象。其中,信号量,互斥体,事件是Windows专门用来进行线程同步的内核对象。dom
在.NET中,有一个WaitHandle抽象类,这个类型封装了一个Windows内核对象句柄,在C#代码中,咱们就可使用WaitHandle类型(确切的说是子类型)的实例进行线程同步了。下面图中显示了WaitHandle类型的全部子类,下面对各个类型进行介绍。函数
WaitHandle类型中的SafeWaitHandle属性就是咱们前面提到的Windows内核对象句柄,WaitHandle是一个抽象类,不能实例化。spa
下面看看WaitHandle中经常使用方法:线程
EventWaitHandle 类容许线程经过发信号互相通讯, 一般状况下,一个或多个线程在 EventWaitHandle 上阻止,直到一个未阻止的线程调用 Set 方法,以释放一个或多个被阻止的线程。3d
在EventWaitHandle类型中,除了父类中的方法,又有本身的特有方法,下面几个是比较经常使用的:code
EventWaitHandle类型有两个子类AutoResetEvent和ManualResetEvent,这两个子类分别表明了EventWaitHandle类型对事件状态的重置模式。在释放单个等待线程后,用 EventResetMode.AutoReset 标志建立的 EventWaitHandle 在终止时会自动重置; 用 EventResetMode.ManualReset 标志建立的 EventWaitHandle 一直保持终止状态,直到它的 Reset 方法被调用。对象
经过EventWaitHandle类型的构造函数,咱们能够经过参数指定EventResetMode,从而选择事件状态的重置模式。固然,咱们也能够直接使用EventWaitHandle的两个子类。blog
public EventWaitHandle(bool initialState, EventResetMode mode); public enum EventResetMode { AutoReset = 0, ManualReset = 1, }
AutoResetEvent 容许线程经过发信号互相通讯:进程
能够经过将一个布尔值传递给构造函数来控制 AutoResetEvent 的初始状态:若是初始状态为终止状态,则为 true;不然为 false。
下面看一个例子:
namespace AutoResetEventTest { class Program { //AutoResetEvent实例初始为非终止状态 private static AutoResetEvent autoResetEvent = new AutoResetEvent(false); static void Main(string[] args) { new Thread(() => { while (true) { //调用WaitOne来等待信号,并设置超时时间为5秒 bool status = autoResetEvent.WaitOne(5000); if (status) { Console.WriteLine("ThreadOne get the signal"); } else { Console.WriteLine("ThreadOne timeout(5 seconds) waiting for signal"); break; } } Console.WriteLine("ThreadOne Exit"); }).Start(); new Thread(() => { while (true) { //调用WaitOne来等待信号,并设置超时时间为5秒 bool status = autoResetEvent.WaitOne(5000); if (status) { Console.WriteLine("ThreadTwo get the signal"); } else { Console.WriteLine("ThreadTwo timeout(5 seconds) waiting for signal"); break; } } Console.WriteLine("ThreadTwo Exit"); }).Start(); Random ran = new Random(); for (int i = 0; i < 8; i++) { Thread.Sleep(ran.Next(500, 1000)); //经过Set向 AutoResetEvent 发信号以释放等待线程 Console.WriteLine("Main thread send the signal"); autoResetEvent.Set(); } Console.Read(); } } }
代码的输出为下,经过结果也能够验证,每次调用Set方法以后,AutoResetEvent 将保持终止状态,直到一个正在等待的线程被释放,而后自动返回非终止状态。
像AutoResetEvent同样,ManualResetEvent 也是线程经过发信号互相通讯:
能够经过将布尔值传递给构造函数来控制 ManualResetEvent 的初始状态,若是初始状态处于终止状态,为 true;不然为 false。
看一个例子:
namespace ManualResetEventTest { class Program { //ManualResetEvent实例初始为非终止状态 private static ManualResetEvent manualResetEvent = new ManualResetEvent(false); static void Main(string[] args) { new Thread(() => { //调用WaitOne来等待信号 manualResetEvent.WaitOne(); Console.WriteLine("Thread get the signal - the first time"); Thread.Sleep(1000); manualResetEvent.WaitOne(); Console.WriteLine("Thread get the signal - the second time"); //调用Reset来以将 ManualResetEvent 置于非终止状态 Console.WriteLine("Child thread reset ManualResetEvent to non-signaled"); manualResetEvent.Reset(); manualResetEvent.WaitOne(); Console.WriteLine("Thread get the signal - the third time"); Console.WriteLine("Child thread reset ManualResetEvent to non-signaled"); manualResetEvent.Reset(); //调用WaitOne来等待信号,并设置超时时间为3秒 manualResetEvent.WaitOne(3000); Console.WriteLine("timeout while waiting for signal"); }).Start(); //经过Set向 ManualResetEvent 发信号以释放等待线程 Console.WriteLine("Main thread set ManualResetEvent to signaled"); manualResetEvent.Set(); Thread.Sleep(3000); Console.WriteLine("Main thread set ManualResetEvent to signaled"); manualResetEvent.Set(); Console.Read(); } } }
代码的输出以下,经过结果验证了,每次调用Set都会将ManualResetEvent设置为终止状态,并释放全部等待线程。只有手动调用 Reset才能将 ManualResetEvent 置于非终止状态。
前一篇文章中介绍的lock和Monitor只能进行同一个进程中的线程同步。
可是,因为同步事件EventWaitHandle是基于内核事件的,因此说,它能够实现进程之间的线程同步。
基于前面AutoResetEvent的例子稍做修改:
class Program { private static EventWaitHandle eventWaitHandle; private static bool newEventWaitHandleObj = true; static void Main(string[] args) { string EventWaitHandleName = "EventWaitHandleTest"; try { //尝试打开已有的同步事件 eventWaitHandle = EventWaitHandle.OpenExisting("EventWaitHandleTest"); newEventWaitHandleObj = false; } catch (WaitHandleCannotBeOpenedException e) { Console.WriteLine("EventWaitHandle named {0} is not exist, error message: {1}", EventWaitHandleName, e.Message); //实例化同步事件,初始为非终止状态,设置为自动重置模式 eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, "EventWaitHandleTest"); Console.WriteLine("Create EventWaitHandle {0}", EventWaitHandleName); newEventWaitHandleObj = true; } new Thread(() => { while (true) { //调用WaitOne来等待信号,并设置超时时间为5秒 bool status = eventWaitHandle.WaitOne(5000); if (status) { Console.WriteLine("ThreadOne get the signal"); } else { Console.WriteLine("ThreadOne timeout(5 seconds) waiting for signal"); break; } } Console.WriteLine("ThreadOne Exit"); }).Start(); new Thread(() => { while (true) { //调用WaitOne来等待信号,并设置超时时间为5秒 bool status = eventWaitHandle.WaitOne(5000); if (status) { Console.WriteLine("ThreadTwo get the signal"); } else { Console.WriteLine("ThreadTwo timeout(5 seconds) waiting for signal"); break; } } Console.WriteLine("ThreadTwo Exit"); }).Start(); if (newEventWaitHandleObj) { Random ran = new Random(); for (int i = 0; i < 8; i++) { Thread.Sleep(ran.Next(500, 1000)); //经过Set向 AutoResetEvent 发信号以释放等待线程 Console.WriteLine("Main thread send the signal"); eventWaitHandle.Set(); } } Console.Read(); } }
代码的输出为下,代码中经过OpenExisting方法尝试打开已存在的同步事件句柄,若是失败,就建立一个EventWaitHandle实例。
至于后面部分代码的工做原理,跟AutoResetEvent的例子彻底同样。
接下来,咱们找到工程生成的exe文件,而后同时启动两次exe文件,能够看到以下输出,后面启动的进程可以打开前面进程建立的同步事件句柄。经过这种方式,就能够实现进程之间的线程同步。
本文介绍了WaitHandle类型,以及该类型的子类型EventWaitHandle,而且介绍了如何经过AutoResetEvent和ManualResetEvent进行线程同步。
下一篇将继续介绍互斥体Mutex和信号量Semaphore的使用。