繁体   English   中英

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

[英]How to keep a .NET console app running?

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

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

我肯定会说第一个。

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

Console.ReadLine();

您可以执行此操作(并删除CancelKeyPress事件处理程序):

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

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

我更喜欢使用Application.Run

static void Main(string[] args) {

   //Do your stuff here

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

   //Cleanup/Before Quit
}

从文档:

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

似乎您正在使它变得比所需的更难。 为什么在发出停止信号后不只是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);
        }
    }
}

前两个比较好

_quitEvent.WaitOne();

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

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

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

token.WaitHandle.WaitOne();

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

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

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

您可以作为主机运行

    public class Program
{
    /// <summary>
    /// Application entry point
    /// </summary>
    /// <param name="args">arguments</param>
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

HostBuilder 在哪里

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseWindowsService()
            .ConfigureServices((_, services) =>
            {
            })
            .ConfigureLogging(config =>
            {
                config.ClearProviders();
            });

我们可以等待应用程序内部抛出的任何异常,而不是读取Console.ReadKey()

program.cs 看起来像

例如:

class Program
{
    static ManualResetEvent _quitEvent = new ManualResetEvent(false);
    static async Task Main(string[] args)
    {
        
        AppDomain currentDomain = AppDomain.CurrentDomain;
        currentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionHandler);

        // code to start the App

        // 

        _quitEvent.WaitOne();
       
    }
    static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
    {
        
        Exception e = (Exception) args.ExceptionObject;
        logger.Error("APP faulted!! " + e.ToString());

        System.Threading.Thread.Sleep(1000);
        _quitEvent.Set();
    }
}

在这种情况下,如果要停止应用程序,则必须终止该应用程序。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM