简体   繁体   中英

Mutex not actually being released?

I have a web api controller that I want to restrict it to run only once at a time. If a new request comes I want it to be rejected and not queued. I've implemented this behaviour using a Mutex, like in the following code:

public IHttpActionResult Get()
{
    string token = Utils.GetCurrentToken(RequestContext);
    //Some irrelevant code

    Task.Factory.StartNew(() =>
    {
        var mutexName = Utils.GetDBFromToken(token).ProjectID.ToString();

        var success = Mutex.TryOpenExisting(mutexName, out Mutex mutex);
        if (!success || mutex == null)
        {
            mutex = new Mutex(true, mutexName);
            Utils.Log(token, mutexName + " - Mutex created");
        }
        else
        {
            Utils.Log(token, mutexName + " - Mutex exists");
            return;
        }

        try
        {
            mutex.WaitOne();

            try
            {
                Utils.Log(token, mutexName + " - Job started");
                Thread.Sleep(10000);
                Utils.Log(token, mutexName + " - Job ended");
            }
            catch (Exception ex)
            {
                //Handle error
            }
        }
        finally
        {
            Utils.Log(token, mutexName + " - About to release mutex");
            if (mutex != null)
            {
                mutex.ReleaseMutex();
                mutex.Close();
                mutex.Dispose();
                Utils.Log(token, mutexName + " - Mutex released");
            }
        }
    });

    return Ok();
}

Now if I call the controller 3 times in success, I get the following logs, which is exactly what I expected:

2018-10-26 11:45:11.650 d6e4dd2e-16f1-43aa-b34b-226187dd9185 - Mutex created
2018-10-26 11:45:11.870 d6e4dd2e-16f1-43aa-b34b-226187dd9185 - Mutex exists
2018-10-26 11:45:11.963 d6e4dd2e-16f1-43aa-b34b-226187dd9185 - Mutex exists
2018-10-26 11:45:12.323 d6e4dd2e-16f1-43aa-b34b-226187dd9185 - Job started
2018-10-26 11:45:22.633 d6e4dd2e-16f1-43aa-b34b-226187dd9185 - Job ended
2018-10-26 11:45:22.947 d6e4dd2e-16f1-43aa-b34b-226187dd9185 - About to release mutex
2018-10-26 11:45:23.290 d6e4dd2e-16f1-43aa-b34b-226187dd9185 - Mutex released

But if I call it once more after release and close has been called, I just get

2018-10-26 11:46:35.133 d6e4dd2e-16f1-43aa-b34b-226187dd9185 - Mutex exists

Shouldn't TryOpenExisting fail after mutex has been released? Am I doing something wrong?

Rather than use a Mutex , you should consider Monitor.TryEnter instead.

It functions like lock , but returns immediately (as you want) if the lock is already taken.

To do this, you will need to declare a static lock object:

private static object lockObject;

Or, if you need multiple locks (eg per database) then you would store those locks in a:

private static ConcurrentDictionary<string, object> lockObjects = new ConcurrentDictionary<string, object>();

getting the lock object using GetOrAdd .

Also, this will not work if you are using a web farm / web garden / load balancer either (since the lock will be process specific). If this is an issue for you, I'd consider using a queue - whereby every web server adds entries to the queue and a single queue consumer pulls off the queue and de-dupes the requests.

Do you really need logic around whether the Mutex exists or not?

Why not always use var mutex = new Mutex(true, mutexName); ? You don't HAVE to check whether it exists or not. And that check of Mutex.TryOpenExisting isn't threadsafe either.

EDIT

One more question, do you need a Mutex here, will a LOCK not be sufficient? Are you planning on running multiple instances of this WebAPI on the same server? If not, then a Lock should be sufficient.

Mutex - Work across Processes

Lock - Work across Threads

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM