如何保持.NET控制台应用程序运行?


104

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

Answers:


63

您总是想避免使用while循环,尤其是在强制代码重新检查变量时。这会浪费CPU资源并减慢程序速度。

我肯定会说第一个。


2
+1。此外,由于bool并不像宣称volatile,有一定的可能性,后续读取到_quitFlagwhile循环将被优化掉,从而导致一个无限循环。
亚当·罗宾逊

2
缺少推荐的方法。我期待这是一个答案。
Iúri多斯安若斯

30

另外,一个更简单的解决方案是:

Console.ReadLine();

我本来建议这样做,但是它不会仅在Ctrl-C上停止
Thomas Levesque 2010年

我得到的印象是,CTRL-C只是一个例子-任何用户输入来关闭它
Cocowalla

请记住,“ Console.ReadLine()”是线程阻塞的。因此,该应用程序仍将在运行,但除了等待用户输入一行外,什么也没做
fabriciorissetto 2015年

2
@fabriciorissetto OP问题指出“启动异步内容”,因此该应用程序将在另一个线程上执行工作
Cocowalla 2015年

1
@Cocowalla我错过了。我的错!
fabriciorissetto

12

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

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

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


我不喜欢您要检查Ctrl + C键,而不是Ctrl + C触发的信号。
CodesInChaos

9

我更喜欢使用Application.Run

static void Main(string[] args) {

   //Do your stuff here

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

   //Cleanup/Before Quit
}

从文档:

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


9
但是,您为此仅依赖于Windows窗体。与传统的.NET框架相比,这并不是什么大问题,但当前的趋势是朝着仅包含所需部分的模块化部署发展。
CodesInChaos

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);
        }
    }
}

2
就我而言,我不控制异步对象运行的线程。
intoOrbit 2010年

1)我不喜欢您要检查Ctrl + C键,而不是Ctrl + C触发的信号。2)如果应用程序使用“任务”而不是单个工作线程,则您的方法将无效。
CodesInChaos

2

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

token.WaitHandle.WaitOne();

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

我已经看到Microsoft.Azure.WebJobs.JobHost使用的这项技术,其中的令牌来自WebJobsShutdownWatcher(结束工作的文件监视程序)的取消令牌源。

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


1
对于需要监听a的任何实际控制台应用程序来说,这是一个很好的答案,CTL+C因为它正在执行长时间运行的操作,或者是一个守护程序,还应正常关闭其工作线程。您可以使用CancelToken做到这一点,因此该答案利用了WaitHandle已经存在的a优势,而不是创建一个新的。
mdisibio

1

前两个比较好

_quitEvent.WaitOne();

因为在第二个线程中,线程每隔一毫秒唤醒一次,这将导致进入OS中断,这很昂贵


Console如果您没有连接控制台(例如,由于该程序由服务启动),则这是方法的一个很好的选择
Marco Sulla

0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.