使用信号量而不是while循环。这是好事还是坏事?

时间:2011-12-01 19:18:49

标签: c# multithreading semaphore

我有一个在其自己的线程中运行的进程,可以在不阻塞的情况下启动/停止。这将最终进入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时没有这个信息。

1 个答案:

答案 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()