1
votes

I'm trying to use C# mutex to ensure my application is running in single instance. So, the application is acquiring global mutex on start, and releasing it on exit. If the mutex acquisition is failed, the error is shown. The application is a console app and it's mostly asynchronous (Main method is async). Basically, the mutex is acquired at the beginning of Main method, and released at the end of it. The problem is that the Main method being asynchronous, its ending may be executed on a different thread than the beginning. So, when I try to release the mutex, I get "Object synchronization method was called from an unsynchronized block of code" exception because the mutex cannot be released from another thread. But I don't use the mutex for inter-thread synchronyzation, I use it for inter-process synchronization. So I don't really care which thread releases the mutex since it's guaranteed that acquire and release are not conflicting with each other. I know there are two ways to avoid this error:

  • Use separate thread for mutex (this thread will acquire the mutex, then block and wait for application to exit, then release the mutex)
  • Use main thread with synchronization context, which will allow to run await continuations on the same thread

Both of these ways seem too cumbersome. Is there a better way to ensure single instance for async app?

2
Are you creating the mutex in a using statement?asaf92
Does this answer your question? Named Mutex with awaitasaf92
Have you tried the suggestion from this answer? It looks pretty nifty. They just create a named EventWaitHandle, and keep it alive for the duration of the app (a GC.KeepAlive(handle) at the end of the app may be needed). There is a comment warning for .NET Core not supporting this solution, but I just tested it on .NET 5 and it works fine.Theodor Zoulias
@TheodorZoulias Yes, looks like it's much more convenient than mutex, thanks. Regarding .net core I believe it's about being non cross-platform. So when you use it under windows, it works, but if you try to run the same code for linux, for example, it will throw PlatformNotSupportedException.Sergey Sokolov

2 Answers

2
votes

While you could use either of the approaches you mention, it would probably just be simpler to use a named semaphore instead of a named mutex.

Unlike mutexes, semaphores do not care which threads release them.

2
votes

You could use a synchronous Main entry point. For a console application, blocking the startup thread is not a big deal. Unlike GUI applications, this thread has nothing to do while asynchronous operations are carried out. This is what happens anyway when you use an async Task Main as entry point.

public static void Main(string[] args)
{
    var mutex = new Mutex(false, Assembly.GetEntryAssembly().GetName().Name);
    if (!mutex.WaitOne(0))
    {
        Console.WriteLine($"Already running. Press any key to continue . . .");
        Console.ReadKey();
        Environment.Exit(1);
    }
    try
    {
        MainAsync(args).GetAwaiter().GetResult();
    }
    finally
    {
        mutex.ReleaseMutex();
    }
}

public static async Task MainAsync(string[] args)
{
    // Main body here
}

Btw you should check out this question about a possible race condition, that could result to an AbandonedMutexException. I guess that the race condition is subtle, because I wasn't able to reproduce it.