【问题标题】:How can I create a System Mutex in C#如何在 C# 中创建系统互斥体
【发布时间】:2011-01-12 07:20:18
【问题描述】:

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

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

【问题讨论】:

标签: c# multithreading mutex


【解决方案1】:

您可以使用 System.Threading.Mutex 类,它有一个 OpenExisting 方法来打开一个命名的系统互斥锁。

这没有回答问题:

如何创建系统/多进程互斥体

要创建系统范围的互斥锁,请调用 System.Threading.Mutex 构造函数,该构造函数将字符串作为参数。这也称为“命名”互斥体。要查看它是否存在,我似乎找不到比 try catch 更优雅的方法:

System.Threading.Mutex _mutey = null;
try
{
    _mutey = System.Threading.Mutex.OpenExisting("mutex_name");
    //we got Mutey and can try to obtain a lock by waitone
    _mutey.WaitOne();
}
catch 
{
    //the specified mutex doesn't exist, we should create it
    _mutey = new System.Threading.Mutex("mutex_name"); //these names need to match.
}

现在,要成为一名优秀的程序员,您需要在结束程序时释放此互斥锁

_mutey.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);

我认为这里的诀窍在于,您似乎有一个实际上没有的选项(对我来说似乎是一个设计缺陷)。如果您为requestInitialOwnership 发送true,有时您无法判断您是否拥有互斥锁。如果您通过true 并且您的调用似乎创建了互斥锁,那么显然您拥有它(由文档确认)。如果您通过true 并且您的调用没有创建互斥锁,那么您只知道互斥锁已经创建,您不知道可能创建互斥锁的其他进程或线程当前是否拥有互斥锁。所以,你必须WaitOne 来确保你拥有它。但是,你做了多少Releases?如果某个其他进程在您获得互斥锁时拥有它,那么只有您对WaitOne 的显式调用需要为Released。如果您对构造函数的调用导致您拥有互斥锁,并且您显式调用了WaitOne,则您将需要两个Releases。

我会把这些话写成代码:

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

【讨论】:

  • .NET Framework 4.5 添加了TryOpenExisting 方法,如果互斥体不存在,该方法不会引发异常。
  • @280Z28 感谢您的提醒。 .NET fx 3.5 具有等效功能(尽管我认为它存在设计缺陷),我在原始答案中没有提及,并且我在上面的编辑二中进行了说明。
  • 只是一个小(但很重要)的细节,当线程而不是进程结束时,Mutex 被放弃。就我而言,我有一个具有多个线程的 WCF 服务,其中一个线程放弃了互斥锁,使多个实例同时运行。见msdn.microsoft.com/en-us/library/…
  • 有点乱。您的最后一个示例实际上只有在没有创建互斥锁的情况下才“有效”,并且互斥锁上没有“发布”方法。但它让想法得到了理解。
【解决方案2】:

您可以使用System.Threading.Mutex 类,它有一个OpenExisting 方法来打开一个命名的系统互斥体。

【讨论】:

  • 谢谢,没有意识到我想要的方法。
【解决方案3】:

我在 Linux 下使用 Mono 使用上述系统互斥体时运气不佳。我可能只是在做一些简单的错误,但如果进程意外退出(kill -9),以下工作效果很好并且可以很好地清理。有兴趣听到 cmets 或批评。

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;

    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-30
    • 1970-01-01
    相关资源
    最近更新 更多