我有一个在其自己的线程中运行的进程,可以在不阻塞的情况下启动/停止。这将最终进入Windows服务,但我现在在控制台应用程序中设置它,直到它完全充实。
在调用Start()之后,我希望主程序线程阻塞,直到按下Ctrl-C。我知道这会奏效:
public static void Main(string[] args)
{
bool keepGoing = true;
var service = new Service();
System.Console.TreatControlCAsInput = false;
System.Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
{
e.Cancel = true;
service.Stop();
keepGoing = false; // Break the while loop below
};
service.Start();
while( keepGoing )
{
Thread.Sleep(100); // 100 is arbitrary
}
}
然而,我发现标志和任意睡眠值令人烦恼。我知道while循环中的CPU成本几乎为0,但我宁愿有一个“硬”块,一旦完成Ctrl-C处理程序就会释放。我设计了下面的内容,使用信号量来阻止,直到完成匿名的Ctrl-C处理程序:
public static void Main(string[] args)
{
var service = new Service();
var s = new Semaphore(1, 1);
System.Console.TreatControlCAsInput = false;
System.Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
{
e.Cancel = true;
service.Stop();
s.Release(); // This will allow the program to conclude below
};
service.Start();
s.WaitOne(); // This will not block
s.WaitOne(); // This will block w/o CPU usage until the sempahore is released
}
这是一个糟糕的设计吗?这有点矫枉过正吗?这很危险吗?
修改
我还挂了AppDomain.CurrentDomain.UnhandledException,如下所示:
AppDomain.CurrentDomain.UnhandledException += delegate {
service.Stop();
s.Release();
};
编辑第二个:
我应该注意,退出时调用Stop()
方法至关重要。 @Adam Ralph对于混合控制台/服务有一个非常好的模式,但在回答Q时没有这个信息。
答案 0 :(得分:4)
我们的一些应用程序中也有类似的要求。它们是Windows服务,但是对于调试,我们经常希望将它们作为控制台应用程序运行。此外,我们通常很早就将新应用程序编写为Windows服务,但是一旦我们证明了这个概念等,我们通常不希望将它们作为服务实际运行,直到稍后。
这是我们使用的模式: -
using (var service = new Service())
{
if (Environment.UserInterActive)
{
service.Start();
Thread.Sleep(Timeout.Infinite);
}
else
{
ServiceBase.Run(service);
}
}
告诉线程无限睡眠可能看起来效率低下,但这只适用于调试方案,冗余线程不需要CPU时间,只需要一些内存(大约1MB),这主要由分配给线程的堆栈空间组成。仍然可以使用Ctrl + C或关闭命令窗口退出该过程。
- 编辑 -
如果您发现在按下Ctrl + C时未调用service.Dispose()
(即发生粗暴中止)并且对Dispose()
的调用至关重要,那么我猜您可以明确地这样做因此: -
using (var service = new Service())
{
if (Environment.UserInterActive)
{
Console.CancelKeyPress += (sender, e) => service.Dispose();
service.Start();
Thread.Sleep(Timeout.Infinite);
}
else
{
ServiceBase.Run(service);
}
}
请注意,Stop()
应封装在Dispose()
。