when a C# program holds a named semaphore, it does not seem to be released when the application is terminated early (for example by pressing Ctrl+C or closing the console window). At least not until all instances of the process have terminated.
With a named mutex an AbandonedMutexException is raised in this case but not with a semaphore. How do you prevent one program instance from stalling when another program instance has been terminated early?
class Program
{
// Same with count > 1
private static Semaphore mySemaphore = new Semaphore(1, 1, "SemaphoreTest");
static void Main(string[] args)
{
try
{
// Blocks forever if the first process was terminated
// before it had the chance to call Release
Console.WriteLine("Getting semaphore");
mySemaphore.WaitOne();
Console.WriteLine("Acquired...");
}
catch (AbandonedMutexException)
{
// Never called!
Console.WriteLine("Acquired due to AbandonedMutexException...");
}
catch (System.Exception ex)
{
Console.WriteLine(ex);
}
Thread.Sleep(20 * 1000);
mySemaphore.Release();
Console.WriteLine("Done");
}
}
In general, you can’t guarantee that a thread releases a semaphore when the thread exits. You can write try/finally blocks and critical finalizers, but those won’t always work if the program terminates abnormally. And, unlike mutexes, other threads won’t be notified if a thread exits while it still holds the semaphore.
The reason is that the Windows semaphore object, on which the .NET Semaphore object is based, doesn’t keep track of which threads have acquired it, and therefore can’t throw an exception similar to the
AbandonedMutexException.That said, you can be notified when the user closes the window. You need to set a control handler to listen to particular events. You call the Windows API function SetConsoleCtrlHandler, passing it a callback function (delegate) that handles the events you’re interested in. It’s been a while since I did this, but in general.
Create a managed prototype for the
SetConsoleCtrlHandlerfunction, and the callback:Now, create your handler method:
And, finally, during initialization you want to set the control handler:
Your control handler will now be called when the user closes the window. That will allow you to release the semaphore or do other cleanup.
You might be interested in my ConsoleDotNet package. I wrote three articles about this stuff, the last two of which are still available at DevSource. I don’t know what happened to the first one.