使用Mutex同步C#对象:在C#对象析构函数中调用ReleaseMutex()

时间:2012-09-21 22:07:58

标签: c# object synchronization mutex interprocess

我有一个应用程序代码,我在其中使用互斥锁在创建对象期间同步某些代码。对象构造函数获取互斥锁,并且仅在不再需要对象时释放它,因此释放互斥锁的一个位置将在对象析构函数中。当我使用应用程序的2个实例调试代码时,第一个实例首先获取互斥锁,第二个实例坐下并等待(mut.WaitOne())。然后用户关闭第一个应用程序实例。在这个例子中,第二个实例mut.WaitOne()抛出异常:“由于被遗弃的互斥锁而等待完成。”这发生在第一个实例中调用mut.ReleaseMutex()之前(我知道它,因为它在调用MutexRelease之前在对象析构函数代码中命中了我的断点)。似乎在调用ReleaseMutex()之前释放了互斥锁,从而导致异常。我如何解决这种竞争条件?谢谢你的帮助。

public sealed class MyObject
{
    static ExtDeviceDriver devDrv;
    private Mutex mut = new Mutex(false,myMutex);

    public MyObject()
    {
        mut.WaitOne();
        //Thread safe code here.
        devDrv = new ExtDeviceDriver();
    }

    ~MyObject()
    {
        mut.ReleaseMutex();
    }
}

2 个答案:

答案 0 :(得分:4)

你的方法有缺陷;这不是你应该如何(尝试)执行同步。如果你想阻止多个应用程序实例...那么这样做,而不是这个。如果您需要同步特定的呼叫,请尽可能在最窄的范围内进行。

我仍然可以通过复制原始引​​用并在另一个线程上使用它进行函数调用来创建一个竞争条件。除了构造函数之外,没有任何东西实际上是同步的,除了创建多个类的实例之外,你不会阻止任何事情,如果我尝试创建第二个实例,我会遇到死锁。不是很好。

研究IDisposable模式。终结者不是确定性的。这不是C ++,它不是析构函数,你不能依赖它来执行它。

其次,你的互斥锁应该是静态的。每个实例都获取自己的互斥锁,因此您在实例1中同步的互斥锁与实例2的互斥锁不同。这需要是共享资源。

来自文档:

  

废弃的互斥锁通常表示代码中存在严重错误。当线程退出但未释放互斥锁时,受互斥锁保护的数据结构可能不处于一致状态。如果可以验证数据结构的完整性,请求互斥锁所有权的下一个线程可以处理此异常并继续。

     

对于系统范围的互斥锁,废弃的互斥锁可能表示应用程序已突然终止(例如,使用Windows任务管理器)。

接下来就本地v系统互斥体说了这个......

  

互斥锁有两种类型:本地互斥锁(未命名)和命名系统互斥锁。 本地互斥锁仅存在于您的进程中。它可以被进程中的任何线程使用,该线程具有对表示互斥锁的Mutex对象的引用。每个未命名的Mutex对象代表一个单独的本地互斥锁。

听起来像你想要一个系统互斥体。如何告诉我们哪些呼叫需要同步,以便我们可以告诉你如何做到这一点?这是一个非常基本的例子:

class Foo
{
    static Mutex _mut(false);
    public MyObject()
    {
        _mut.WaitOne();
        //Thread safe code here.
        devDrv = new ExtDeviceDriver();
        _mut.ReleaseMutex();
    }

    public void SomeSynchronizedMethod()
    {
        // synchronize this call
        _mut.WaitOne();
        devDrv.DoSomething();
        _mut.ReleaseMutex();
    }
}

答案 1 :(得分:2)

析构函数不是确定性的; CLR无法保证何时运行。相反,在您的类中实现IDisposable,并强制调用者在using(...)块中使用其实例。这将确保在需要时调用Dispose的实现。

如,

public sealed class MyObject : IDisposable
{
    static ExtDeviceDriver devDrv;
    private Mutex mut = new Mutex(false,myMutex);

    public MyObject()
    {
        mut.WaitOne();
        //Thread safe code here.
        devDrv = new ExtDeviceDriver();
    }

    public void Dispose() {
        mut.ReleaseMutex();
    }

}

然后来电者只需要这样做:

using (var x = new MyObject()) {
    // etc
}

当执行流程存在using块时,Dispose将被调用,无论例外或其他任何内容。