捕获取消事件后程序未完成

Program not finishing after trapping Cancel event

为什么按下 Ctrl+C 时此控制台应用程序不退出?

程序输出:

Press Ctrl+C to stop...
doing stuff.
doing stuff.
...
*Ctrl+C pressed*
exiting...
*never actually exits*

class Program {
    static void Main(string[] args) {
        MainAsync(args).GetAwaiter().GetResult();
    }

    private static async Task MainAsync(string[] args) {

        MyAsyncClass myAsync = new MyAsyncClass();

        var tcs = new TaskCompletionSource<object>();
        Console.CancelKeyPress += (sender, e) => { tcs.SetResult(null); };

        var task = Task.Run(() => myAsync.Start());

        await Console.Out.WriteLineAsync("Press Ctrl+C to stop...");

        await tcs.Task;
        await Console.Out.WriteLineAsync("exiting...");
    }
}

public class MyAsyncClass {
    public async Task Start() {
        while(true) {
            await Console.Out.WriteLineAsync("doing stuff.");
            Thread.Sleep(1000);
        }
    }
}

您需要将 ConsoleCancelEventArgs.Cancel 属性 设置为 true:

Console.CancelKeyPress += (sender, e) =>
{
    tcs.SetResult(null);
    e.Cancel = true;   // <-------- add this to your code
};

这将允许您的代码继续执行到程序结束并正常退出,而不是 Ctrl+C 在事件处理程序完成后尝试终止应用程序。

请注意,在测试中,我发现这似乎只在附加 Visual Studio 调试器时才重要(运行 F5)。但是 运行out 一个附件(Ctrl+F5,或者只是 运行 已编译的 .exe)似乎并不关心这个 属性 是否被设置。我找不到任何信息来解释为什么会出现这种情况,但我的猜测是存在某种竞争条件。

最后,将 CancellationToken 传递到您的 myAsync.Start 方法并使用它代替 while(true) 是一种很好的形式。使用 await Task.Delay 而不是 Thread.Sleep 也会更好(但这些都不是问题的根源)。