Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Global Mutex with Async Await [duplicate]

I have a critical section of code that should be run by only one process at a time. To ensure only one process on the machine is ever running this critical block I use a global Mutex. The code in the critical section uses async/await. Overall the code looks something like this:

var mutex = new Mutex(false, @"Global\MyMutexName");    
mutex.WaitOne(-1, false);   
SetupASystemWideResource() 
await DoSomeAsyncWorkOnThatResource();    
mutex.ReleaseMutex();

The issue is that the code after the await can happen on a different thread. So the mutex could get released on a different thread than it was acquired. This causes an exception

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

Normally this would make since you dont want a different thread releasing the mutex than acquiring it. However in my case I am really only interested in preventing a 2nd instance of the application from setting up the system resource in a different way while the first instance is using it. The resource itself is fine to be used from multiple threads within the one instance.

What is the correct way to ensure this and also do the work asynchronously?

like image 585
innominate227 Avatar asked Feb 05 '19 03:02

innominate227


1 Answers

If i understand the problem, and this is a console app or there is no synchronisation context, just call Wait, there is no fear of a deadlock and it should work fine

var mutex = new Mutex(false, @"Global\MyMutexName");    
mutex.WaitOne(-1, false);

DoSomeAsyncWorkOnThatResource().Wait();
mutex.ReleaseMutex();

Or if you really must (and more controversially) you could use

Task.Run(async () => await DoSomeAsyncWorkOnThatResource()).Wait();

The other thing you can do is use a custom Scheduler like CurrentThreadTaskScheduler (first time i have seen you use for it), as described in Stephen Toubs Parallel Programming with .NET. Nuget ParallelExtensionsExtras

runs all tasks on the current thread when scheduling is requested

And use it with Task.Factory.StartNew or something that takes a scheduler

like image 195
TheGeneral Avatar answered Sep 29 '22 01:09

TheGeneral