【问题标题】:How to keep a .NET console app running?如何保持 .NET 控制台应用程序运行?
【发布时间】:2011-02-04 21:48:49
【问题描述】:

考虑一个控制台应用程序,它在单独的线程中启动一些服务。它需要做的就是等待用户按 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
}

【问题讨论】:

    标签: c# multithreading sleep manualresetevent


    【解决方案1】:

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

    我肯定会说第一个。

    【讨论】:

    • +1。此外,由于bool 未声明为volatile,因此在while 循环中对_quitFlag 的后续读取很可能会被优化掉,从而导致无限循环。
    • 缺少推荐的方法。我期待这是一个答案。
    【解决方案2】:

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

    Console.ReadLine();
    

    【讨论】:

    • 我正要建议,但它不会只在 Ctrl-C 上停止
    • 我的印象是 CTRL-C 只是一个例子 - 任何用户输入来关闭它
    • 记住'Console.ReadLine()' 是线程阻塞。所以应用程序仍然会运行,但除了等待用户输入一行之外什么都不做
    • @fabriciorissetto OP 问题声明“启动异步内容”,因此应用程序将在另一个线程上执行工作
    • @Cocowalla 我错过了。我的错!
    【解决方案3】:

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

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

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

    【讨论】:

    • 我不喜欢你检查的是 Ctrl+C 键,而不是 Ctrl+C 触发的信号。
    【解决方案4】:

    我更喜欢使用 Application.Run

    static void Main(string[] args) {
    
       //Do your stuff here
    
       System.Windows.Forms.Application.Run();
    
       //Cleanup/Before Quit
    }
    

    来自文档:

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

    【讨论】:

    • 但是你依赖于 windows 窗体只是为了这个。传统的 .NET 框架并没有太大问题,但目前的趋势是模块化部署,只包括您需要的部分。
    【解决方案5】:

    也可以根据取消令牌阻塞线程/程序。

    token.WaitHandle.WaitOne();
    

    WaitHandle 在令牌被取消时发出信号。

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

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

    【讨论】:

    • 对于任何需要监听CTL+C 的实际控制台应用程序来说,这是一个很好的答案,因为它正在执行一个长时间运行的操作,或者是一个守护进程,它也应该优雅地关闭它的工作线程。您可以使用 CancelToken 来执行此操作,因此此答案利用了已经存在的 WaitHandle,而不是创建一个新的。
    【解决方案6】:

    看起来你让它变得比你需要的更难。在你发出停止信号后,为什么不直接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);
            }
        }
    }
    

    【讨论】:

    • 在我的情况下,我不控制运行异步内容的线程。
    • 1) 我不喜欢您检查的是 Ctrl+C 键,而不是 Ctrl+C 触发的信号。 2) 如果应用程序使用任务而不是单个工作线程,则您的方法不起作用。
    【解决方案7】:

    前两个比较好

    _quitEvent.WaitOne();
    

    因为在第二个线程中,每隔一毫秒唤醒一次的线程会被转为昂贵的 OS 中断

    【讨论】:

    • 如果您没有连接控制台(例如,程序由服务启动),这是Console 方法的一个很好的替代方案
    【解决方案8】:

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

    【讨论】:

      猜你喜欢
      • 2010-10-16
      • 2010-11-17
      • 2011-02-10
      • 1970-01-01
      • 2017-01-02
      • 2013-02-11
      • 1970-01-01
      • 2018-02-09
      • 2018-06-18
      相关资源
      最近更新 更多