如何在 C# 中创建系统互斥体

问题描述 投票:0回答:3

如何创建系统/多进程互斥体来使用相同的非托管资源协调多个进程。

背景: 我编写了一个使用文件打印机的程序,该打印机一次只能由一个进程使用。如果我想在计算机上运行的多个程序上使用它,我需要一种方法来跨系统同步它。

c# multithreading mutex
3个回答
31
投票

您可以使用 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


3
投票

您可以使用

System.Threading.Mutex
类,该类具有
OpenExisting
方法来打开命名系统互斥体。


0
投票

我在 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;

    }
}
© www.soinside.com 2019 - 2024. All rights reserved.