125

考虑一个控制台应用程序,它在单独的线程中启动一些服务。它需要做的就是等待用户按 Ctrl+C 将其关闭。

以下哪项是更好的方法?

static ManualResetEvent _quitEvent = new ManualResetEvent(false);

static void Main() {
    Console.CancelKeyPress += (sender, eArgs) => {
        _quitEvent.Set();
        eArgs.Cancel = true;
    };

    // kick off asynchronous stuff 

    _quitEvent.WaitOne();

    // cleanup/shutdown and quit
}

或者这个,使用 Thread.Sleep(1):

static bool _quitFlag = false;

static void Main() {
    Console.CancelKeyPress += delegate {
        _quitFlag = true;
    };

    // kick off asynchronous stuff 

    while (!_quitFlag) {
        Thread.Sleep(1);
    }

    // cleanup/shutdown and quit
}
4

8 回答 8

77

您总是希望避免使用 while 循环,尤其是当您强制代码重新检查变量时。它会浪费 CPU 资源并减慢您的程序。

我肯定会说第一个。

于 2010-04-06T16:48:55.793 回答
34

或者,一个更简单的解决方案就是:

Console.ReadLine();
于 2010-04-06T16:49:17.850 回答
13

您可以这样做(并删除CancelKeyPress事件处理程序):

while(!_quitFlag)
{
    var keyInfo = Console.ReadKey();
    _quitFlag = keyInfo.Key == ConsoleKey.C
             && keyInfo.Modifiers == ConsoleModifiers.Control;
}

不确定这是否更好,但我不喜欢Thread.Sleep循环调用的想法。我认为阻止用户输入更干净。

于 2010-04-06T16:51:25.460 回答
7

我更喜欢使用Application.Run

static void Main(string[] args) {

   //Do your stuff here

   System.Windows.Forms.Application.Run();

   //Cleanup/Before Quit
}

来自文档:

开始在当前线程上运行标准应用程序消息循环,没有表单。

于 2014-11-10T11:46:12.703 回答
7

也可以基于取消令牌阻塞线程/程序。

token.WaitHandle.WaitOne();

当令牌被取消时,会发出 WaitHandle 信号。

我已经看到 Microsoft.Azure.WebJobs.JobHost 使用了这种技术,其中令牌来自 WebJobsShutdownWatcher(结束作业的文件观察器)的取消令牌源。

这可以控制程序何时结束。

于 2019-01-22T05:21:41.850 回答
4

似乎你让它变得比你需要的更难。为什么不只是Join在你发出信号停止后的线程?

class Program
{
    static void Main(string[] args)
    {
        Worker worker = new Worker();
        Thread t = new Thread(worker.DoWork);
        t.IsBackground = true;
        t.Start();

        while (true)
        {
            var keyInfo = Console.ReadKey();
            if (keyInfo.Key == ConsoleKey.C && keyInfo.Modifiers == ConsoleModifiers.Control)
            {
                worker.KeepGoing = false;
                break;
            }
        }
        t.Join();
    }
}

class Worker
{
    public bool KeepGoing { get; set; }

    public Worker()
    {
        KeepGoing = true;
    }

    public void DoWork()
    {
        while (KeepGoing)
        {
            Console.WriteLine("Ding");
            Thread.Sleep(200);
        }
    }
}
于 2010-04-07T17:43:58.710 回答
1

前两个比较好

_quitEvent.WaitOne();

因为在第二个线程中,每隔一毫秒唤醒一次线程将被转入操作系统中断,这很昂贵

于 2010-04-06T16:50:06.503 回答
0

你应该像在编写 Windows 服务时那样做。您永远不会使用 while 语句,而是使用委托。WaitOne() 通常在等待线程处理时使用 - Thread.Sleep() - 不可见 - 您是否想过使用 System.Timers.Timer 使用该事件来检查关闭事件?

于 2010-04-06T17:22:47.770 回答