当一个C#程序拥有一个命名信号量时,它似乎不会在应用程序提前终止时释放(例如按Ctrl + C或关闭控制台窗口)。至少直到该过程的所有实例都已终止。
使用已命名的互斥锁,在这种情况下会引发AbandonedMutexException,但不会引发信号量。当另一个程序实例提前终止时,如何防止一个程序实例停止?
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");
}
}
答案 0 :(得分:4)
通常,无法保证线程在线程退出时释放信号量。您可以编写try / finally块和关键终结器,但如果程序异常终止,那些并不总是有效。并且,与互斥锁不同,如果线程在仍然保持信号量的情况下退出,则不会通知其他线程。
原因是.NET Semaphore对象所基于的Windows semaphore object不会跟踪哪些线程获取它,因此不能抛出类似{{1}的异常}}
也就是说,当用户关闭窗口时,可以收到通知。您需要设置控制处理程序以侦听特定事件。你调用Windows API函数SetConsoleCtrlHandler,传递一个回调函数(委托)来处理你感兴趣的事件。自从我这样做以来已经有一段时间了,但总的来说。
为AbandonedMutexException
函数和回调创建托管原型:
SetConsoleCtrlHandler
现在,创建处理程序方法:
/// <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's 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);
最后,在初始化期间,您需要设置控制处理程序:
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;
}
现在,当用户关闭窗口时,将调用您的控制处理程序。这将允许您释放信号量或进行其他清理。
您可能对我的ConsoleDotNet package感兴趣。我写了三篇关于这个东西的文章,最后两篇文章仍然可以在DevSource上找到。我不知道第一个发生了什么。
答案 1 :(得分:2)
你可以写mySemaphore.Release();在类析构函数中
class Program
{
~Program() // destructor
{
mySemaphore.Release();
}
}
或者在try \ catch
中添加一个finally pharsetry{}
catch{}
finally
{
mySemaphore.Release();
}
如果您使用的是asp.net,还可以使用位于Global.asax.cs的Application_End
protected void Application_End(Object sender, EventArgs eventArgs)
{
mySemaphore.Release();
}