参考地址:http://blog.sina.com.cn/s/blog_68e4d2910100q6uj.htmlhtml
什么是Mutex编程
“mutex”是术语“互相排斥(mutually exclusive)”的简写形式,也就是互斥量。互斥量跟临界区中提到的Monitor很类似,只有拥有互斥对象的线程才具备访问资源的权限,因为互斥对象只有一个,所以就决定了任何状况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其余线程在得到后得以访问资源。互斥量比临界区复杂,由于使用互斥不只仅可以在同一应用程序不一样线程中实现资源的安全共享,并且能够在不一样应用程序的线程之间实现对资源的安全共享。.Net中mutex由Mutex类来表示。浏览器
先绕一小段路安全
在开始弄明白Mutex如何使用以前,咱们要绕一小段路再回来。服务器
读书的时候,你们接触互斥量、信号量这些玩意儿应该是在《操做系统》这一科。因此,其实这些玩意儿出现的起因是做为OS功能而存在。来看看Mutex的声明:网络
[ComVisibleAttribute(true)]
public sealed class Mutex : WaitHandle多线程
因而咱们不得再也不走远一些,看看WaitHandel的声明:app
[ComVisibleAttribute(true)]
public abstract class WaitHandle : MarshalByRefObject, IDisposableide
WaitHandle实现了一个接口,又继承了一个父类。IDisposable在C#线程同步(2)- 临界区&Monitor关于Using的题外话中已简单提到,这里就再也不多说了。看看它的父类MarshalByRefObject:函数
MarshalByRefObject 类
容许在支持远程处理的应用程序中跨应用程序域边界访问对象。
……
备注:
应用程序域是一个操做系统进程中一个或多个应用程序所驻留的分区。同一应用程序域中的对象直接通讯。不一样应用程序域中的对象的通讯方式有两种:一种是跨应用程序域边界传输对象副本,一种是使用代理交换消息。
MarshalByRefObject 是经过使用代理交换消息来跨应用程序域边界进行通讯的对象的基类。……
好啦,剩下的内容不用再看,不然就绕得太远了。咱们如今知道Mutex是WaitHandle的子类(偷偷地告诉你,之后要提到的EventWaitHandle、信号量Semaphore也是,而AutoResetEvent和ManualResetEvent则是它的孙子),而WaitHandle又继承自具备在操做系统中跨越应用程序域边界能力的MarshalByRefObject类。因此咱们如今能够获得一些结论:
有点象Monitor?不如当它是lock。
好了,终于绕回来了。来看看怎么使用Mutex。
回想咱们在《C#线程同步(2)- 临界区&Monitor》中提到的关于生产者和消费者的场景,因为这两个函数不等效于Monitor的Wait()和Pulse(),因此仅靠这ReleaseMutex()和WaitOne()两个方法Mutex还没法适用于咱们那个例子。
固然Mutext上还“算有”其它一些用于同步通知的方法,但它们都是其父类WaitHandle上的静态方法。所以它们并非为Mutex特地“度身订作”的,与Mutex使用的方式有些不搭调(你能够尝试下用Mutex替换Monitor实现咱们以前的场景看看),或者说Mutex实际上是有些不情愿的拥有这些方法。咱们会在下一篇关于EventWaitHandle的Blog中再深刻一些地讨论Mutex和通知的问题。这里暂且让咱们放一放,直接借用MSDN上的示例来简单说明Mutex的最简单的应用场景吧:
// This example shows how a Mutex is used to synchronize access
// to a protected resource. Unlike Monitor, Mutex can be used with
// WaitHandle.WaitAll and WaitAny, and can be passed across
// AppDomain boundaries.
using System;
using System.Threading;
class Test
{
// Create a new Mutex. The creating thread does not own the
// Mutex.
private static Mutex mut = new Mutex();
private const int numIterations = 1;
private const int numThreads = 3;
static void Main()
{
// Create the threads that will use the protected resource.
for(int i = 0; i < numThreads; i++)
{
Thread myThread = new Thread(new ThreadStart(MyThreadProc));
myThread.Name = String.Format("Thread{0}", i + 1);
myThread.Start();
}
// The main thread exits, but the application continues to
// run until all foreground threads have exited.
}
private static void MyThreadProc()
{
for(int i = 0; i < numIterations; i++)
{
UseResource();
}
}
// This method represents a resource that must be synchronized
// so that only one thread at a time can enter.
private static void UseResource()
{
// Wait until it is safe to enter.
mut.WaitOne();
Console.WriteLine("{0} has entered the protected area",
Thread.CurrentThread.Name);
// Place code to access non-reentrant resources here.
// Simulate some work.
Thread.Sleep(500);
Console.WriteLine("{0} is leaving the protected area\r\n",
Thread.CurrentThread.Name);
// Release the Mutex.
mut.ReleaseMutex();
}
}
虽然这只是一个示意性的实例,可是我仍然不得不由于这个示例中没有使用try/finally来保证ReleaseMutex的执行而表示对微软的鄙视。对于一个初学的人来讲,第一个看到的例子可能会永远影响这我的使用的习惯,因此是否在简单示意的同时,也能“简单地”给你们show一段足够规范的代码?更况且有至关部分的人都是直接copy sample code……一边告诫全部人Abandoned Mutexes的危害,一边又给出一段一个异常就能够轻易引起这种错误的sample,MSDN不可细看。
我不得不说Mutex的做用于其说象Monitor不如说象lock,由于它只有等效于Monitro.Enter()/Exit()的做用,不一样之处在于Mutex请求的锁就是它本身。正由于如此,Mutex是能够也是必须(不然哪来的锁?)被实例化的,而不象Monitor是个Static类,不能有本身的实例。
全局和局部的Mutex
若是在一个应用程序域内使用Mutex,固然不如直接使用Monitor/lock更为合适,由于前面已经提到Mutex须要更大的开销而执行较慢。不过Mutex毕竟不是Monitor/lock,它生来应用的场景就应该是用于进程间同步的。
除了在上面示例代码中没有参数的构造函数外,Mutex还能够被其它的构造函数所建立:
另外,Mutex还有两个重载的OpenExisting()方法能够打开已经存在的Mutex。
Mutex的用途
如前所述,Mutex并不适合于有相互消息通知的同步;另外一方面而咱们也屡次提到局部Mutex应该被Monitor/lock所取代;而跨应用程序的、相互消息通知的同步由将在后面讲到的EventWaiteHandle/AutoResetEvent/ManualResetEvent承担更合适。因此,Mutex在.net中应用的场景彷佛很少。不过,Mutex有个最多见的用途:用于控制一个应用程序只能有一个实例运行。
using System;
using System.Threading;
class MutexSample
{
private static Mutex mutex = null; //设为Static成员,是为了在整个程序生命周期内持有Mutex
static void Main()
{
bool firstInstance;
mutex = new Mutex(true, @"Global\MutexSampleApp", out firstInstance);
try
{
if (!firstInstance)
{
Console.WriteLine ("已有实例运行,输入回车退出……");
Console.ReadLine();
return;
}
else
{
Console.WriteLine ("咱们是第一个实例!");
for (int i=60; i > 0; --i)
{
Console.WriteLine (i);
Thread.Sleep(1000);
}
}
}
finally
{
//只有第一个实例得到控制权,所以只有在这种状况下才须要ReleaseMutex,不然会引起异常。
if (firstInstance)
{
mutex.ReleaseMutex();
}
mutex.Close();
mutex = null;
}
}
}
这是一个控制台程序,你能够在编译后尝试一次运行多个程序,结果固然老是只有一个程序在倒数计时。你可能会在互联网上找到其它实现应用程序单例的方法,好比利用 Process 查找进程名、利用Win32 API findwindow 查找窗体的方式等等,不过这些方法都不能保证绝对的单例。由于多进程和多线程是同样的,因为CPU时间片随机分配的缘由,可能出现多个进程同时检查到没有其它实例运行的情况。这点在CPU比较繁忙的状况下容易出现,现实的例子好比傲游浏览器。即使你设置了只容许一个实例运行,当系统比较忙的时候,只要你尝试屡次打开浏览器,那就有可能“幸运”的打开若干独立的浏览器窗口。
别忘了,要实现应用程序的单例,须要在在整个应用程序运行过程当中都保持Mutex,而不仅是在程序初始阶段。因此,例子中Mutex的创建和销毁代码包裹了整个Main()函数。
题外话:
很奇怪,Mutex的父类WaitHandle实现了IDisposable,可是咱们在Mutex上却找不到Dispose()方法,因为这个缘由上面代码的finally中咱们用的是Close()来释放Mutex所占用的资源。其实,这里的Close()就等效于Dispose(),可这是为何? 再去看看WaitHandle,咱们发现它实现的Disopose()方法是protected的,所以咱们没有办法直接调用它。而它公开了一个Close()方法给调用者们用于替代Dispose(),所以Mutex上也就只有Close()。可这又是为何? 话说.Net最初的设计师是微软从Borland公司挖过来的,也就是Delphi之父。熟悉Delphi的人都知道,Object Pascal构架中用于释放资源的方法就是Dispose(),因此Dispose()也成为.Net构架中的重要的一员。 不过从语义上来说,对于文件、网络链接之类的资源“Close”比“Dispose”更符合咱们的习惯。所以“体贴”的微软为了让用户(也就是咱们这些写代码的人)更“舒服”,在这种语义上更适合用Close的资源上,老是提供Close()做为Disopose()的公共实现。其实Close()内部不过是直接调用Dispose()而已。对于这种作法,我在感动之余实在以为有些多余了,到底要把一个东西搞得多么变幻无穷才肯罢休? 若是你实在喜欢Dispose(),那么能够用向上转型 ((IDisposable)((WaitHandle)mutex)).Dispose()把它找出来。即强制把mutex转换为WaitHandle,而后再把WaitHandle强制转型为IDisposable,而IDisposable上的Dispose()是public的。不过咱们终究并不肯定Mutex以及WaitHandle的Close()中究竟是不是在override的时候加入了什么逻辑,因此仍是老老实实用Close()好了~