C#多线程:深入了解线程同步lock,Monitor,Mutex,同步事件和等待句柄(中)
本篇继续介绍WaitHandler类及其子类 Mutex,ManualResetEvent,AutoResetEvent的用法。.NET中线程同步的方式多的让人看了眼花缭乱,究竟该怎么去理解呢?其实,我们抛开.NET环境看线程同步,无非是执行两种操作:一是互斥/加锁,目的是保证临界区代码操作的“原子性”;另一种是信号灯操作,目的是保证多个线程按照一定顺序执行,如生产者线程要先于消费者线程执行。.NET中线程同步的类无非是对这两种方式的封装,目的归根结底都可以归结为实现互斥/ 加锁或者是信号灯这两种方式,只是它们的适用场合有所不。下面我们根据类的层次结构了解WaitHandler及其子类。 
      1.WaitHandler 
      WaitHandle是Mutex,Semaphore,EventWaitHandler,AutoResetEvent,ManualResetEvent共同的祖先,它封装Win32同步句柄内核对象,也就是说是这些内核对象的托管版本。 
      线程可以通过调用WaitHandler实例的方法WaitOne在单个等待句柄上阻止。此外,WaitHandler类重载了静态方法,以等待所有指定的等待句柄都已收集到信号WaitAll,或者等待某一指定的等待句柄收集到信号WaitAny。这些方法都提供了放弃等待的超时间隔、在进入等待之前退出同步上下文的机会,并允许其它线程使用同步上下文。WaitHandler是C#中的抽象类,不能实例化。 
      2.EventWaitHandler vs. ManualResetEvent vs. AutoResetEvent(同步事件) 
      我们先看看两个子类ManualResetEvent和AutoResetEvent在.NET Framework中的实现:
- //.NET Framework中ManualResetEvent类的实现
- [ComVisible(true), HostProtection(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)]
- public sealed class ManualResetEvent : EventWaitHandle
- {
- // Methods
- public ManualResetEvent(bool initialState) : base(initialState, EventResetMode.ManualReset)
- {
- }
- }
- //.NET Framework中AutoResetEvent类的实现
- [ComVisible(true), HostProtection(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)]
- public sealed class AutoResetEvent : EventWaitHandle
- {
- // Methods
- public AutoResetEvent(bool initialState)
- : base(initialState, EventResetMode.AutoReset)
- {
- }
- }
原来ManualResetEvent和AutoResetEvent都继承自EventWaitHandler,它们的唯一区别就在于父类 EventWaitHandler的构造函数参数EventResetMode不同,这样我们只要弄清了参数EventResetMode值不同时,EventWaitHandler类控制线程同步的行为有什么不同,两个子类也就清楚了。为了便于描述,我们不去介绍父类的两种模式,而直接介绍子类。 
      ManualResetEvent和AutoResetEvent的共同点: 
      1)Set方法将事件状态设置为终止状态,允许一个或多个等待线程继续;Reset方法将事件状态设置为非终止状态,导致线程阻止;WaitOne阻止当前线程,直到当前线程的WaitHandler收到事件信号。 
      2)可以通过构造函数的参数值来决定其初始状态,若为true则事件为终止状态从而使线程为非阻塞状态,为false则线程为阻塞状态。 
      3)如果某个线程调用WaitOne方法,则当事件状态为终止状态时,该线程会得到信号,继续向下执行。 
      ManualResetEvent和AutoResetEvent的不同点: 
      1)AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号后,AutoResetEvent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只有继续等待,也就是说AutoResetEvent一次只唤醒一个线程; 
      2)ManualResetEvent则可以唤醒多个线程,因为当某个线程调用了ManualResetEvent.Set()方法后,其他调用WaitOne的线程获得信号得以继续执行,而ManualResetEvent不会自动将信号置为不发送。 
      3)也就是说,除非手工调用了ManualResetEvent.Reset()方法,则ManualResetEvent将一直保持有信号状态,ManualResetEvent也就可以同时唤醒多个线程继续执行。 
      示例场景:张三、李四两个好朋友去餐馆吃饭,两个人点了一份宫爆鸡丁,宫爆鸡丁做好需要一段时间,张三、李四不愿傻等,都专心致志的玩起了手机游戏,心想宫爆鸡丁做好了,服务员肯定会叫我们的。服务员上菜之后,张三李四开始享用美味的饭菜,饭菜吃光了,他们再叫服务员过来买单。我们可以从这个场景中抽象出来三个线程,张三线程、李四线程和服务员线程,他们之间需要同步:服务员上菜—>张三、李四开始享用宫爆鸡丁—>吃好后叫服务员过来买单。这个同步用什么呢? ManualResetEvent还是AutoResetEvent?通过上面的分析不难看出,我们应该用ManualResetEvent进行同步,下面是程序代码: 
张三李四吃饭的故事
- public class EventWaitTest
- {
- private string name; //顾客姓名
- //private static AutoResetEvent eventWait = new AutoResetEvent(false);
- private static ManualResetEvent eventWait = new ManualResetEvent(false);
- private static ManualResetEvent eventOver = new ManualResetEvent(false);
- public EventWaitTest(string name)
- {
- this.name = name;
- }
- public static void Product()
- {
- Console.WriteLine("服务员:厨师在做菜呢,两位稍等");
- Thread.Sleep(2000);
- Console.WriteLine("服务员:宫爆鸡丁好了");
- eventWait.Set();
- while (true)
- {
- if (eventOver.WaitOne(1000, false))
- {
- Console.WriteLine("服务员:两位请买单");
- eventOver.Reset();
- }
- }
- }
- public void Consume()
- {
- while (true)
- {
- if (eventWait.WaitOne(1000, false))
- {
- Console.WriteLine(this.name + ":开始吃宫爆鸡丁");
- Thread.Sleep(2000);
- Console.WriteLine(this.name + ":宫爆鸡丁吃光了");
- eventWait.Reset();
- eventOver.Set();
- break;
- }
- else
- {
- Console.WriteLine(this.name + ":等着上菜无聊先玩会手机游戏");
- }
- }
- }
- }
- public class App
- {
- public static void Main(string[] args)
- {
- EventWaitTest zhangsan = new EventWaitTest("张三");
- EventWaitTest lisi = new EventWaitTest("李四");
- Thread t1 = new Thread(new ThreadStart(zhangsan.Consume));
- Thread t2 = new Thread(new ThreadStart(lisi.Consume));
- Thread t3 = new Thread(new ThreadStart(EventWaitTest.Product));
- t1.Start();
- t2.Start();
- t3.Start();
- Console.Read();
- }
- }
编译后查看运行结果,符合我们的预期,控制台输出为: 
      服务员:厨师在做菜呢,两位稍等... 
      张三:等着上菜无聊先玩会手机游戏 
      李四:等着上菜无聊先玩会手机游戏 
      张三:等着上菜无聊先玩会手机游戏 
      李四:等着上菜无聊先玩会手机游戏 
      服务员:宫爆鸡丁好了 
      张三:开始吃宫爆鸡丁 
      李四:开始吃宫爆鸡丁 
      张三:宫爆鸡丁吃光了 
      李四:宫爆鸡丁吃光了 
      服务员:两位请买单 
      如果改用AutoResetEvent进行同步呢?会出现什么样的结果?恐怕张三和李四就要打起来了,一个享用了美味的宫爆鸡丁,另一个到要付账的时候却还在玩游戏。感兴趣的朋友可以把注释的那行代码注释去掉,并把下面一行代码注释掉,运行程序看会出现怎样的结果。 
       3.Mutex(互斥体) 
       Mutex和EventWaitHandler有着共同的父类WaitHandler类,它们同步的函数用法也差不多,这里不再赘述。Mutex的突出特点是可以跨应用程序域边界对资源进行独占访问,即可以用于同步不同进程中的线程,这种功能当然这是以牺牲更多的系统资源为代价的。 
      这种跨进程同步的一种应用是,限制同一台电脑中同时打开两个相同的程序。具体实现可以参考《用Mutex或进程限制用户在一台电脑上同时打开两个程序》。
C#多线程:深入了解线程同步lock,Monitor,Mutex,同步事件和等待句柄(中)的更多相关文章
- 【转】多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(上)
		本篇从Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler的类关系图开始,希望通过 本篇的介绍能对常见的线程同步方法有一个整体的认识,而对 ... 
- C# 多线程(lock,Monitor,Mutex,同步事件和等待句柄)
		本篇从 Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler 的类关系图开始,希望通过本篇的介绍能对常见的线程同步方法有一个整体的认识,而 ... 
- 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock
		[源码下载] 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLoc ... 
- C#多线程同步事件及等待句柄AutoResetEvent 和 ManualResetEvent
		最近捣鼓了一下多线程的同步问题,发现其实C#关于多线程同步事件处理还是很灵活,这里主要写一下,自己测试的一些代码,涉及到了AutoResetEvent 和 ManualResetEvent,当然还有也 ... 
- C#各种同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEvent
		看下组织结构: System.Object System.MarshalByRefObject System.Threading.WaitHandle System.Threading.Mutex S ... 
- python类库32[多进程同步Lock+Semaphore+Event]
		python类库32[多进程同步Lock+Semaphore+Event] 同步的方法基本与多线程相同. 1) Lock 当多个进程需要访问共享资源的时候,Lock可以用来避免访问的冲突. imp ... 
- 01 语言基础+高级:1-7 异常与多线程_day07 【线程池、Lambda表达式】
		day07[线程池.Lambda表达式] 主要内容 等待与唤醒案例 线程池 Lambda表达式 教学目标 -[ ] 能够理解线程通信概念-[ ] 能够理解等待唤醒机制-[ ] 能够描述Java中线程池 ... 
- JAVA语言规范-线程和锁章节之同步、等待和通知
		JAVA语言规范:线程和锁 1 同步 java编程语言提供了线程间通信的多种机制.这些方法中最基本的是同步化,此方法是使用监视器实现的.JAVA中每个对象与一个监视器相关联,一个线程可以加锁和解锁监视 ... 
- .NET多线程之线程安全,Lock(锁)、Monitor(同步访问)、LazyInitializer(延迟初始化)、Interlocked(原子操作)、static(静态)构造函数、volatile、
		1.什么是线程安全 线程安全是编程中的术语,指某个函数.函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成.一般来说,线程安全的函数应该为每个调用它的线程分配专门的 ... 
随机推荐
- Android 短信模块分析(二) MMS中四大组件核心功能详解
			接下来的分析先从MMS中四大组件(Activity ,BroadCastReceiver,Service,ContentProvider),也是MMS中最核心的部分入手: 一. Activity 1 ... 
- 横瓜从WP7与WP8不兼容之处预测微软公司在未来20年将会倒闭
			横瓜(601069289) 12:40:50 微软频繁升级操作系统版本捞钱,还不向下兼容,其企业的核心竞争力不再是技术,是钱!除了操作系统,其它产品没有任何核心技术和竞争力,微软必死. 横瓜从WP7 ... 
- Cocos2d-x--Box2D使用GLES-Render.h渲染查看刚体
			分为两部分:文件拷贝和代码实现 1.文件拷贝: 在TestCpp下找到GLES-Render.h和GLES-Render.cpp两个文件 复制到G:\cocos2d-2.1rc0-x-2.1.3\co ... 
- javadataAbout stack and heap in JAVA(2)
			改章节个人在上海喝咖啡的时候突然想到的...近期就有想写几篇关于javadata的笔记,所以回家到之后就奋笔疾书的写出来发表了 The stack is much faster than the he ... 
- windows下练习linux shell
			<---开始学习linux---记录一下---路漫漫其修远兮---加油吧---萌萌达> 使用软件:Cygwin 下载地址(免安装版):链接: http://pan.baidu.com/s ... 
- HDU 2064 菜鸡第一次写博客
			果然集训就是学长学姐天天传授水铜的动态规划和搜索,今天讲DP由于困意加上面瘫学长"听不懂就是你不行"的呵呵传授,全程梦游.最后面对连入门都算不上的几道动态规划,我的内心一片宁静,甚 ... 
- Kubernetes(k8s)容器运行时(CRI)
			Kubernetes节点的底层由一个叫做"容器运行时"的软件进行支撑,它负责比如启停容器这样的事情.最广为人知的容器运行时当属Docker,但它不是唯一的.事实上,容器运行时这个领 ... 
- 安装Java Cer证书
			skldfsdf Javaawtetwetwetwetwe testse Java estestset 
- 距离VR时代的真正到来还有多久?
			2016年被称为是VR元年,各大VR设备商的宣传攻势铺天盖地,众VR产品看的人眼花缭乱.随着平民化进程不断推进以及渗透率的提升,VR成为近两年来最引人关注的焦点,在众多领域的共同作用下,VR时代是否即 ... 
- php学习笔记——基础知识(2)
			9.PHP语句 if 语句 - 如果指定条件为真,则执行代码 if...else 语句 - 如果条件为 true,则执行代码:如果条件为 false,则执行另一端代码 if...else if.... ... 
