3

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?

6
  • Does this answer your question? What is a good pattern for using a Global Mutex in C#? Commented Dec 17, 2020 at 15:12
  • Are you creating the mutex in a using statement? Commented Dec 17, 2020 at 15:14
  • 1
    Does this answer your question? Named Mutex with await Commented Dec 17, 2020 at 15:19
  • 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. Commented Dec 17, 2020 at 18:19
  • @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. Commented Dec 17, 2020 at 22:54

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Well, actually it's a little bit more complex than I described in the initial question. The mutex is released in async deinitialize method which is defined in another library. And this library is also used from WPF application, and no problem there because GUI thread uses synchronization context. So even if I block in Main, that method would still use await and its continuation would run on another thread.
@SergeySokolov I see. Then installing a suitable SynchronizationContext seems like the only option. You could use the AsyncContext from this package (documentation). You'll have to put all your code inside an AsyncContext.Run(async () => { /* ... */ }) call. It's a blocking call, much like the analogous Application.Run(new Form1());.
3

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.

5 Comments

Looks like there is no reliable way to guarantee release of a named semaphore or to handle "abandoned" case like with mutexes. For example, if the app is killed, the next time it starts, it'll think that another instance is still running.
@ssokolov: Good point, and these behaviors are related. The entire concept of an "abandoned mutex" exists precisely because the release has to happen on the same thread. Semaphores don't care about threads; thus there is no concept of an "abandoned semaphore". If you need to handle abandoned primitives, then you'll have to stick with mutex and have a specific thread acquire/release it.
What if I just remove release call altogether? Sure, next time I try to acquire, I get abandoned mutex exception, but it can be easily handled and it does not prevent from acquiring the mutex. I mean, could there be any undesirable side effects to this way?
@SergeySokolov: I can't think of any downsides to that.
@SergeySokolov, the other answer about using named events probably suits your original usecase better, but just for completeness, the approach of never releasing the mutex would also come with problems, because if you acquire it on some random threadpool worker, you have no control over its lifetime. The thread may get released later as the threadpool rotates through threads while your program is still running. If you happen to acquire it on the original "Main Thread" (that is, before awaiting anything), it may work, as that thread blocks on Main's Task, but that is undocumented and may change

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.