如何创建系统/多进程互斥体来使用相同的非托管资源协调多个进程。
背景: 我编写了一个使用文件打印机的程序,该打印机一次只能由一个进程使用。如果我想在计算机上运行的多个程序上使用它,我需要一种方法来跨系统同步它。
您可以使用 System.Threading.Mutex 类,该类具有 OpenExisting 方法来打开命名的系统互斥体。
这并不能回答问题:
如何创建系统/多进程互斥体
要创建系统范围的互斥锁,请调用以字符串作为参数的
System.Threading.Mutex
构造函数。这也称为“命名”互斥体。要看看它是否存在,我似乎找不到比 try catch 更优雅的方法:
System.Threading.Mutex _mutex = null;
try
{
_mutex = System.Threading.Mutex.OpenExisting("mutex_name");
//we got Mutex and can try to obtain a lock by WaitOne
_mutex.WaitOne();
}
catch
{
//the specified mutex doesn't exist, we should create it
_mutex = new System.Threading.Mutex("mutex_name"); //these names need to match.
}
现在,要成为一名优秀的程序员,您需要在结束程序时释放此互斥体
_mutex.ReleaseMutex();
或者,您可以保留它,在这种情况下,当您的线程退出时,它将被称为“放弃”,并允许另一个进程创建它。
[编辑]
作为描述被放弃的互斥体的最后一句的旁注,当另一个线程获取互斥体时,将抛出异常
System.Threading.AbandonedMutexException
,告诉他发现它处于被放弃的状态。
[编辑二]
我不知道为什么几年前我会这样回答这个问题;有(并且曾经)有一个构造函数重载,它可以更好地检查现有互斥体。事实上,我给出的代码似乎有竞争条件! (对你们没有纠正我感到羞耻!:-P )
这是竞争条件:想象两个进程,它们都尝试同时打开现有的互斥体,并且都到达代码的 catch 部分。然后,其中一个进程创建了互斥体,从此幸福地生活在一起。然而,另一个进程尝试创建互斥体,但这一次它已经创建了!这种互斥体的检查/创建需要是原子的。
http://msdn.microsoft.com/en-us/library/bwe34f1k(v=vs.90).aspx
所以...
var requestInitialOwnership = false;
bool mutexWasCreated;
Mutex m = new Mutex(requestInitialOwnership,
"MyMutex", out mutexWasCreated);
我认为这里的技巧是,你似乎有一个实际上没有的选项(对我来说看起来像是一个设计缺陷)。如果您为
true
发送 requestInitialOwnership
,有时您无法判断您是否拥有互斥体。如果您通过 true
并且看起来您的调用创建了互斥锁,那么显然您拥有它(已通过文档确认)。如果您传递 true
并且您的调用没有创建互斥体,您只知道互斥体已经创建,您不知道可能创建互斥体的其他进程或线程当前是否拥有该互斥体。因此,您必须WaitOne
以确保您拥有它。但是,你做了多少个 Release
呢?如果其他进程在您获取互斥锁时拥有该互斥锁,则只需对 WaitOne
进行显式调用即可 Release
d。如果您对构造函数的调用导致您拥有互斥体,并且您显式调用了 WaitOne
,则您将需要两个 Release
。
我会把这些话写进代码中:
var requestInitialOwnership = true; /*This appears to be a mistake.*/
bool mutexWasCreated;
Mutex m = new Mutex(requestInitialOwnership,
"MyMutex", out mutexWasCreated);
if ( !mutexWasCreated )
{
bool calledWaitOne = false;
if ( ! iOwnMutex(m) ) /*I don't know of a method like this*/
{
calledWaitOne = true;
m.WaitOne();
}
doWorkWhileHoldingMutex();
m.Release();
if ( calledWaitOne )
{
m.Release();
}
}
由于我没有找到测试您当前是否拥有互斥锁的方法,因此我强烈建议您将
false
传递给构造函数,以便您知道您不拥有互斥锁,并且知道多少次打电话给Release
。
我在 Linux 下使用 Mono 时使用上述系统互斥体的运气并不好。我可能只是做了一些简单的错误,但以下内容运行良好,并且如果进程意外退出(kill -9),则可以很好地清理。有兴趣听到评论或批评。
class SocketMutex{
private Socket _sock;
private IPEndPoint _ep;
public SocketMutex(){
_ep = new IPEndPoint(IPAddress.Parse( "127.0.0.1" ), 7177);
_sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
_sock.ExclusiveAddressUse = true; // most critical if you want this to be a system wide mutex
}
public bool GetLock(){
try{
_sock.Bind(_ep); // 'SocketException: Address already in use'
}catch(SocketException se){
Console.Error.WriteLine ("SocketMutex Exception: " se.Message);
return false;
}
return true;
}
}