本随笔续接:.NET 同步与异步 之 线程安全的集合 (十一)
本随笔 及 接下来的两篇随笔,将介绍 .NET 同步与异步系列 的最后一个大块知识点:WaitHandle家族。
抽象基类:WaitHandle, 三个子类: EventWaitHandle(Event通知) 、Mutex(进程同步锁)、Semaphone (信号量),还有两个孙子辈:System.Threading.AutoResetEvent、System.Threading.ManualResetEvent,都是 EventWaitHandle 的子类。
一、抽象基类 WaitHandle
[ComVisibleAttribute(true)]public abstract class WaitHandle : MarshalByRefObject, IDisposable
通过上面的信息,我们可以知道 WaitHandle 继承自 MarshalByRefObject, 并实现了 IDisposable 接口。
对于 MarshalByRefObject ,你也许不是很熟悉,但它的很多子类你一定会用过的,让我们来揭开它的庐山真面目。
在MSND中是这样描述 MarshalByRefObject 的:
看到这里你也许更迷惑了,我用过它? 用过它的子类? 没错,就是用过它的子类,并且还很多。
例如 System.Drawing命名空间的 Brush、Image、Pen、Font 等等,还有个大家更熟悉的 System.IO命名空间下的Stream.
延展阅读:利用 MarshalByRefObject 实现 AOP 。
看到这里我们只需要知道 WaitHandle 具有跨应用程序域进行通讯的能力就可以了。
二、Mutex(进程同步锁)
1、MSDN对Mutex的定义为进程间的同步基元, 即锁的概念。
反观Monitor、平时只用来在应用程序域内的线程之间通信。其实,如果用于锁的对象派生自MarshalByRefObject,Monitor 也可在多个应用程序域中提供锁定。
Mutex由于需要调用操作系统资源,因此执行的开销比Monitor大得多,所以如果仅仅需要在应用程序内部的线程间同步操作,Monitor/lock应当是首选
2、Mutex 的用法
WaitOne() /WaitOne(TimeSpan, Boolean)及若干个重载:请求所有权,该调用会一直阻塞到当前 mutex 收到信号,或直至达到可选的超时间隔,这几个方法都不需要提供锁定对象作为额外参数。
您可以使用 WaitHandle.WaitOne 请求所有权的互斥体的方法。 调用线程受到阻止,直到发生下列情况之一︰
互斥体发出信号以指示不拥有。 在此情况下, WaitOne 方法将返回 true, ,调用线程的互斥体所有权,并访问由 mutex 保护的资源。 线程完成后访问资源,必须调用 ReleaseMutex 方法来释放 mutex 的所有权。
对调用中指定的超时间隔 WaitOne 具有方法 millisecondsTimeout 或 timeout 参数已过。 在此情况下, WaitOne 方法将返回 false, 此时该线程不会获取互斥体的所有权。
ReleaseMutex():释放当前 Mutex 一次。注意,这里强调了一次,因为拥有互斥体的线程可以在重复的调用WaitOne系列函数而不会阻止其执行;这个跟Monitor的Enter()/Exit()可以在获取对象锁后可以被重复调用一样。Mutex被调用的次数由公共语言运行库(CLR)保存,每WaitOne()一次计数+1,每ReleaseMutex()一次计数-1,只要这个计数不为0,其它Mutex的等待者就会认为这个Mutex没有被释放,也就没有办法获得该Mutex。 另外,跟Monitor.Exit()一样,只有Mutex的拥有者才能RleaseMutex(),否则会引发异常。
如果线程在拥有互斥体时终止,我们称此互斥体被遗弃(Abandoned)。在MSDN里,微软以警告的方式指出这属于“严重的”编程错误。这是说拥有mutex的拥有者在获得所有权后,WaitOne()和RelaseMutex()的次数不对等,调用者自身又不负责任地中止,造成mutex 正在保护的资源可能会处于不一致的状态。其实,这无非就是提醒你记得在try/finally结构中使用Mutex。
3、全局和局部的Mutex
如果在一个应用程序域内使用Mutex,当然不如直接使用Monitor/lock更为合适,因为前面已经提到Mutex需要更大的开销而执行较慢。不过Mutex毕竟不是Monitor/lock,它生来应用的场景就应该是用于进程间同步的。用于在进程间通讯的Mutex我们称为全局Mutex,而只用于在应用程序域内部通讯的Mutex、我们称为局部Mutex.
全局Mutex和局部Mutex是通过构造函数来构造不同的实例的,让我们来看一下Mutex的构造函数,一共有5个,挑两个具有代表性的看一下吧:
Mutex():用无参数的构造函数得到的Mutex没有任何名称,而进程间无法通过变量的形式共享数据,所以没有名称的Mutex也叫做局部(Local)Mutex。另外,这样创建出的Mutex,创建者对这个实例并没有拥有权,仍然需要调用WaitOne()去请求所有权。
Mutex(Boolean initiallyOwned, String name, out Booldan createdNew, MutexSecurity):第一个bool参数:指示初始化的实例是否拥有互斥体所有权。第二个string类型、为互斥体指定一个名称,如果string为null或者空字符串 则相当于创建一个没有名字的Mutex,当属于局部Mutex. 而有名字的Mutex当属于全局Mutex.第三个bool参数、如果已经初始化了互斥体 返回True, 如果互斥体已经存在则返回False. 最后一个参数用于Mutex访问的安全性控制。
4、用途
Mutex天生为进程间的同步基元,因此它可以用来控制应用程序的单实例:
/// <summary>/// 单实例运行/// </summary>/// <returns> true 应用程序已启动,false 则没有 </returns>public bool SingleRun(ref System.Threading.Mutex mutex ) { mutex = new System.Threading.Mutex(false, "WINDOWS"); if (!mutex.WaitOne(0, false)) { mutex.Close(); mutex = null; } if (mutex == null) { return true; } return false; }
以上就是详解.NET 同步与异步 之 Mutex的详细内容,更多请关注Work网其它相关文章!