【问题标题】:Capturing CancelKeyPress to stop an async console app at a safe point捕获 CancelKeyPress 以在安全点停止异步控制台应用程序
【发布时间】:2018-06-21 17:04:33
【问题描述】:

我正在开发一个小型实用控制台应用程序,内置于 C# 7.1(支持 async Main)。

应用程序接受几个输入命令之一,然后开始一个长时间运行的过程,该过程迭代数以万计的项目,处理每一个。

我希望能够随时取消这个过程(使用 CTRL+C),虽然程序不应该立即取消,而是应该完成当前迭代,然后停止。

这是我目前所拥有的缩短版。

private static bool _cancel;    

private static async Task Main(string[] args)
{
    Console.CancelKeyPress += (sender, eventArgs) =>
                                      {
                                          eventArgs.Cancel = true;                                              _logger("Migration will be stopped after the current record has been completed.\n");
                                          _cancel = true;
                                      };

    while (!_cancel)
    {
        var input = Console.ReadLine();

        // handle the various input commands
    }
}

在运行(可选)长时间运行的进程的方法中,有一个检查这个全局 _cancel 变量的逻辑:

private static async Task RunPersonMigration(Order order)
{
    var nextPerson = // ...
    while (nextPerson.IsValid)
    {
        // business logic

        if (_cancel)
        {
            _logger("Person migration stopped by user.\n");
            return;
        }

        nextPerson = // ...
    }
}

但是,每当我按 CTRL+C 时,Visual Studio 调试器都会要求我定位程序集,而且每次通常都是不同的程序集。例如,我被要求找到 waithandle.cs 和 thread.cs。因为我无法找到此类文件,正在运行的调试过程突然停止。

我永远看不到是哪一行导致了问题,而且断点的数量也无济于事。

基本上,我正在尝试使用 CTRL+C 退出长时间运行的进程而不退出控制台应用程序。

谁能告诉我应该如何正确处理在我选择的时间点取消长时间运行的控制台进程?

更新:
如果我更新我的 CancelKeyPress 委托...

Console.CancelKeyPress += (sender, eventArgs) =>
                                  {
                                      **eventArgs.Cancel = true;**                                              _logger("Migration will be stopped after the current record has been completed.\n");
                                      _cancel = true;
                                  };

然后这会阻止程序崩溃到关闭,但我仍然希望能够捕获 CTRL+C 并将其用作退出长时间运行的进程的一种方式退出控制台应用程序本身。这甚至可能吗?

【问题讨论】:

  • 不清楚你在问什么。 Ctrl-C 在控制台程序中具有特殊处理,包括在您按下键时中断进程,调试器有义务通知您。 VS 不要求你找到源文件。您可以在该点继续执行程序。您甚至可以在没有源文件的情况下调试汇编代码。从你的描述来看,好像并没有什么问题。如果您不希望调试器在按键被按下时介入,不要在附加了调试器的情况下运行程序!
  • 如果我在控制台应用程序中启动一个长时间运行的进程,如何允许用户通过命令停止该进程而不是退出整个控制台?如何传入“停止您当前正在运行的进程”?
  • 从您非常模糊的描述来看,听起来您只是未能在CancelKeyPress 事件处理程序中将e.Cancel 属性设置为true。如果你不这样做,Ctrl-C 仍然会终止程序,即使你处理了事件。有关详细信息,请参阅我建议的副本。
  • “我仍然希望在进程停止后保持控制台应用程序打开。我不确定这是否可能” -- 如果你设置了e.Canceltrue,唯一剩下的就是终止你的程序是你自己的代码。您的Main() 方法中有一个循环,该循环一直运行到您设置_cancel 标志,然后您的程序退出。如果你不想让你的程序退出,那就不要退出

标签: c# asynchronous console console-application cancellation


【解决方案1】:

使用CancellationToken 的工作示例,可以向下传递:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp
{
    class Program
    {
        // Cancellation Tokens - https://docs.microsoft.com/en-us/previous-versions/dd997289(v=vs.110)
        private static readonly CancellationTokenSource canToken = new CancellationTokenSource();

        static async Task Main(string[] args)
        {
            Console.WriteLine("Application has started. Ctrl-C to end");

            Console.CancelKeyPress += (sender, eventArgs) =>
            {
                Console.WriteLine("Cancel event triggered");
                canToken.Cancel();
                eventArgs.Cancel = true;
            };

            await Worker();

            Console.WriteLine("Now shutting down");
            await Task.Delay(1000);
        }

        async static Task Worker()
        {
            while (!canToken.IsCancellationRequested)
            {
                // do work       
                Console.WriteLine("Worker is working");
                await Task.Delay(1000); // arbitrary delay
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-19
    • 2019-09-01
    • 2015-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多