In general, you cannot guarantee that a thread will release a semaphore when a thread exits. You can write try / finally blocks and critical finalizers, but they will not always work if the program terminates abnormally. And, unlike mutexes, other threads will not be notified if the thread exits while it still holds the semaphore.
The reason is that the Windows semaphore object on which the .NET Semaphore object is based does not keep track of which threads got it and therefore cannot throw an exception like AbandonedMutexException .
However, you can receive notifications when the user closes the window. You need to configure a handler to listen for specific events. You call the Windows API function SetConsoleCtrlHandler , passing it a callback (delegate) function that processes events of interest to you. I did it, but overall.
Create a managed prototype for the SetConsoleCtrlHandler function and callback:
/// <summary> /// Control signals received by the console control handler. /// </summary> public enum ConsoleControlEventType: int { /// <summary> /// A CTRL+C signal was received, either from keyboard input or from a /// signal generated by the GenerateConsoleCtrlEvent function. /// </summary> CtrlC = 0, /// <summary> /// A CTRL+BREAK signal was received, either from keyboard input or from /// a signal generated by GenerateConsoleCtrlEvent. /// </summary> CtrlBreak = 1, /// <summary> /// A signal that the system sends to all processes attached to a console /// when the user closes the console (either by clicking Close on the console /// window window menu, or by clicking the End Task button command from /// Task Manager). /// </summary> CtrlClose = 2, // 3 and 4 are reserved, per WinCon.h /// <summary> /// A signal that the system sends to all console processes when a user is logging off. /// </summary> CtrlLogoff = 5, /// <summary> /// A signal that the system sends to all console processes when the system is shutting down. /// </summary> CtrlShutdown = 6 } /// <summary> /// Control event handler delegate. /// </summary> /// <param name="CtrlType">Control event type.</param> /// <returns>Return true to cancel the control event. A return value of false /// will terminate the application and send the event to the next control /// handler.</returns> public delegate bool ConsoleCtrlHandlerDelegate(ConsoleControlEventType CtrlType); [DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetConsoleCtrlHandler( ConsoleCtrlHandlerDelegate HandlerRoutine, bool Add);
Now create a handler method:
private static bool ConsoleCtrlHandler(ConsoleControlEventType CtrlType) { switch (CtrlType) { case CtrlClose:
And finally, during initialization, you want to install a control handler:
SetConsoleCtrlHandler(ConsoleControlHandler);
Now your handler will be called when the user closes the window. This will allow you to free the semaphore or perform another cleanup.
You might be interested in my ConsoleDotNet package . I have written three articles about this material, the last two of which are still available in DevSource. I do not know what happened to the first.