Named Mutex with Await

Named Mutex with await

You must ensure that mutex is being accessed consistently on a certain thread. You could do that in a number of ways:

  1. Do not use await in the critical section during which you hold the mutex
  2. Invoke the mutex calls on a TaskScheduler that only has a single thread

That could look like this:

await Task.Factory.StartNew(() => mutex.WaitOne(), myCustomTaskScheduler);

Or, you use synchronous code and move everything to the thread-pool. If you only have access to an async version of DoSomething, consider just calling Task.Wait on its result. You'll suffer a minor inefficiency here. Probably fine.

Calling await operation after acquiring mutex

This is a job for a monitor (or to make this more async-friendly, a semaphore), not a mutex.

The problem is that the continuation to WriteTextAsync is likely to run on a separate thread, so it can't release the mutex - that can only be done from the same thread that originally acquired the mutex.

var semaphore = new SemaphoreSlim(1);

await semaphore.WaitAsync();

try
{
await FileIO.WriteTextAsync(filename, text);
Debug.WriteLine("written");
}
finally
{
semaphore.Release();
}

How to use named mutexes and async/await in .NET Core 3.1?

The linked solution only works when using a named mutex to synchronize asynchronous code across processes. It won't work to synchronize code within the same process. Mutexes allow recursive acquisition, so by moving all acquisitions on the same thread, it's the same as if the mutex isn't there at all.

I'd have to keep all my semaphores in a Dictionary<string, SemaphoreSlim> and that would be too cumbersome to manage.

If you need a non-recursive named mutex, named Semaphores (which don't work on Linux) or managing your own dictionary is really the only way to go.

I have an AsyncCache<T> that I've been working on but isn't prod-ready yet. It tries to look like a cache of Task<T> instances but is actually a cache of TaskCompletionSource<T> instances.

Wrapping a Mutex with IDisposable and testing it but the test never ends

This happens because of await. After your await Task.Delay(..) you might no longer be on the same thread you were before await statement. So in some cases you are trying to release your mutex from the thread which does not own it - hence your problem. That's easy to verify by writing current thread before and after await:

class Program {
public static void Main() {
while (true) {
var sw = Stopwatch.StartNew();

var task1 = Task.Run(async () => {
using (new NamedMutex("foo")) {
Console.WriteLine("first before await: " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(TimeSpan.FromSeconds(2));
Console.WriteLine("first after await: " + Thread.CurrentThread.ManagedThreadId);
}
});

var task2 = Task.Run(async () => {
using (new NamedMutex("foo")) {
Console.WriteLine("second before await: " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(TimeSpan.FromSeconds(1));
Console.WriteLine("second after await: " + Thread.CurrentThread.ManagedThreadId);
}
});

Task.WaitAll(task1, task2);

//Assert.IsTrue(sw.Elapsed.TotalSeconds >= 5);
Console.WriteLine(sw.Elapsed);
}
}
}


Related Topics



Leave a reply



Submit