Mutex release issues in ASP.NET C# code

2019-07-04 17:12发布

问题:

I'm not exactly sure how to address this issue. I have a mutex that is declared as such:

public class MyNamedLock
{
    private Mutex mtx;
    private string _strLkName;

    public MyNamedLock(string strLockName)
    {
        _strLkName = strLockName;
        //...

        mtx = new Mutex(false, _strLkName, out bCreatedNew, mSec);
    }

    public bool enterLockWithTimeout(int nmsWait = 30 * 1000)
    {
        _nmsWaitLock = nmsWait;

        //Wait
        return mtx.WaitOne(nmsWait);
    }

    public void leaveLock()
    {
        _nmsWaitLock = 0;

        //Release it
        mtx.ReleaseMutex();
    }
}

Then it is used in an ASP.NET page as such:

public class MyClass
{
    private MyNamedLock gl;

    public MyClass()
    {
        gl = new MyNamedLock("lock name");

    }


    public void funct()
    {
        try
        {
            //Enter lock
            if (gl.enterLockWithTimeout())
            {
                //Do work
            }
            else
                throw new Exception("Failed to enter lock");
        }
        finally
        {
            //Leave lock
            gl.leaveLock();
        }
    }
}

This code doesn't give me any trouble in my dev environment but in the production it sometimes throws this exception:

Object synchronization method was called from an unsynchronized block of code.

The description is kinda vague, but just doing the trace I found out that the exception is raised at the mtx.ReleaseMutex(); part. What does it mean and how to fix it?

回答1:

You have some issues on your class, and on the way you use it.

  1. You must release the mutex only if you have previous locked (and this is your error)
  2. You need to Close and Dispose your opened mutex
  3. Also is better to create it just before you going to use it and not when you create you class MyClass.

So I suggest at first look to change your class as:

public class MyNamedLock
{
    private Mutex mtx = null;
    private string _strLkName;

    // to know if finally we get lock
    bool cNeedToBeRelease = false;

    public MyNamedLock(string strLockName)
    {
        _strLkName = strLockName;
        //...

        mtx = new Mutex(false, _strLkName, out bCreatedNew, mSec);
    }

    public bool enterLockWithTimeout(int nmsWait = 30 * 1000)
    {
        _nmsWaitLock = nmsWait;
        bool cLock = false;
        try
        {
            cLock = mtx.WaitOne(nmsWait, false);
            cNeedToBeRelease = cLock;    
        }
        catch (AbandonedMutexException)
        {
            // http://stackoverflow.com/questions/654166/wanted-cross-process-synch-that-doesnt-suffer-from-abandonedmutexexception
            // http://msdn.microsoft.com/en-us/library/system.threading.abandonedmutexexception.aspx
            cNeedToBeRelease = true;
        }
        catch (Exception x)
        {
            // log the error
            Debug.Fail("Check the reason of fail:" + x.ToString());
        }            

        return cLock;        
    }

    public void leaveLock()
    {
        _nmsWaitLock = 0;

        if (mtx != null)
        {
            if (cNeedToBeRelease)
            {
                try
                {
                    mtx.ReleaseMutex();
                    cNeedToBeRelease = false;    
                }
                catch (Exception x)
                {
                    Debug.Fail("Check the reason of fail:" + x.ToString());
                }
            }

            mtx.Close();
            mtx.Dispose();
            mtx = null;
        }
    }
}

This the way you must call that class:

public class MyClass
{
    public MyClass()
    {
    }


    public void funct()
    {
        var gl = new MyNamedLock("lock name");
        try
        {
            //Enter lock
            if (gl.enterLockWithTimeout())
            {
                //Do work
            }
            else
                throw new Exception("Failed to enter lock");
        }
        finally
        {
            //Leave lock
            gl.leaveLock();
        }
    }
}


回答2:

In your finally block you're releasing the mutex regardless of whether you actually acquired it in your try block.

In

try
{
    //Enter lock
    if (gl.enterLockWithTimeout())
    {
        //Do work
    }
    else throw new Exception("Failed to enter lock");
}
finally
{
    //Leave lock
    gl.leaveLock();
}

if gl.enterLockWithTimeout returns false, you will throw an exception but then try to release the lock in the finally block.