Abandoned named semaphore not released

when a C # program contains a named semaphore, it does not seem to be released when the application finishes earlier (for example, by pressing Ctrl + C or closing the console window). At least until all process instances have completed.

With the named mutex, an AbandonedMutexException is thrown in this case, but not with a semaphore. How can you not stop one instance of a program if another instance of the program was completed earlier?

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"); } } 
+4
source share
2 answers

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: // handle it here break; case CtrlBreak: // handle it here break; } // returning false ends up calling the next handler // returning true will prevent further handlers from being called. return false; } 

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.

+4
source

You can write mySemaphore.Release (); in class destructor

 class Program { ~Program() // destructor { mySemaphore.Release(); } } 

or finally add pharse to your try \ catch

 try{} catch{} finally { mySemaphore.Release(); } 

If you are using asp.net, you can also use Application_End, which is located in Global.asax.cs

 protected void Application_End(Object sender, EventArgs eventArgs) { mySemaphore.Release(); } 
+2
source

Source: https://habr.com/ru/post/1396219/


All Articles