I have an application code in which I use a mutex to synchronise some code during the creation of an object. The object constructor acquires the mutex and ONLY releases it when the object is no longer needed thus one place to release the mutex would be in the object destructor. As I debugged the code using 2 instances of the app, the 1st instance first acquires the mutex, the 2nd instance sits and waits (mut.WaitOne()). User then closes the 1st app instance. At this instance, the 2nd instance mut.WaitOne() throws the exception: "The wait completed due to an abandoned mutex." This happens before the mut.ReleaseMutex() was called in the 1st instance (I know it because it hit my breakpoint in the object destructor code before calling MutexRelease). It appears that the mutex was released before ReleaseMutex() was called thus causing the exception. How would I resolve this race condition? Thank you for your help.

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();
    }
}
有帮助吗?

解决方案

Destructors are not deterministic; there is no guarantee by the CLR as to when they will run. Instead, implement IDisposable in your class, and force callers to use its instances in using(...) blocks. That will ensure that your implementation of Dispose gets called when it needs to.

E.g.,

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();
    }

}

Then callers would just need to do this:

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

When the execution flow exists the using block, Dispose will be called, regardless of exceptions or anything else.

其他提示

Your approach is flawed; this is not how you should be (attempting to) perform synchronization. If you want to prevent multiple application instances... then do that, not this. If you need to synchronize specific calls then do so at the most narrow scope possible.

I can still create a race condition in your approach simply by copying the original reference and making function calls with it on another thread. Nothing is actually synchronized aside from the constructor, you are not preventing anything but creating more than a single instance of your class, and if I attempt to create a second instance, I get a deadlock. Not very nice.

Research the IDisposable pattern. Finalizers are not deterministic. This is not C++, that is not a destructor, you cannot rely upon it executing when you want it to.

Secondly, your mutex should be static. Each instance is getting their own mutex, so the mutex you synchronized around in instance 1 is different than that of instance 2. This needs to be a shared resource.

From the docs:

An abandoned mutex often indicates a serious error in the code. When a thread exits without releasing the mutex, the data structures protected by the mutex might not be in a consistent state. The next thread to request ownership of the mutex can handle this exception and proceed, if the integrity of the data structures can be verified.

In the case of a system-wide mutex, an abandoned mutex might indicate that an application has been terminated abruptly (for example, by using Windows Task Manager).

And it goes on to say this regarding local v system mutexes...

Mutexes are of two types: local mutexes, which are unnamed, and named system mutexes. A local mutex exists only within your process. It can be used by any thread in your process that has a reference to the Mutex object that represents the mutex. Each unnamed Mutex object represents a separate local mutex.

It sounds to me like you want a system mutex. How about telling us which calls need to be synchronized so that we can show you how to do it? Here is a very basic example:

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();
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top