为什么 CancellationTokenSource 挂起应用程序

Ale*_*kiy 2 .net c# asynchronous task

这是挂起且永不结束的简单代码段:

public static void Main()
{
    using (var cancellationTokenSource = new CancellationTokenSource())
    {
        Console.CancelKeyPress += (_, __) => 
            cancellationTokenSource.Cancel();
        while (!cancellationTokenSource.Token.WaitHandle.WaitOne(1000))
        {
            Console.WriteLine("Still running...");
        }
        Console.WriteLine("Cancellation is requested. Trying to dispose cancellation token...");
    }
    Console.WriteLine("Just before close");
}
Run Code Online (Sandbox Code Playgroud)

问题是Just before close线条永远不会被执行。我的第一个想法是“可能是因为 KeyPress 关闭了”所以我用以下方式重写了它:

public static void Main()
{
    using (var cancellationTokenSource = new CancellationTokenSource())
    {
        void Foo(object sender, ConsoleCancelEventArgs consoleCancelEventArgs)
        {
            cancellationTokenSource.Cancel();
        }
        Console.CancelKeyPress += Foo;
        while (!cancellationTokenSource.Token.WaitHandle.WaitOne(1000))
        {
            Console.WriteLine("Still running...");
        }
        Console.WriteLine("Cancellation is requested. Unsubscribing...");
        Console.CancelKeyPress -= Foo;
        Console.WriteLine("Cancellation is requested. Trying to dispose cancellation token...");
    }
    Console.WriteLine("Just before close");
}
Run Code Online (Sandbox Code Playgroud)

但现在它挂在Unsubscribing中风......

知道为什么会这样吗?我想运行一些后台任务并等到它在控制台应用程序中完成,但由于描述的原因,我的应用程序刚刚损坏。

Jam*_*rpe 5

问题不在于您的取消令牌,而在于您选择使用CancelKeyPress. 当这个事件发生时,你会得到ConsoleCancelEventArgs,它有一个Cancel属性

获取或设置一个值,该值指示是否同时按下 Control 修饰键和 C 控制台键 (Ctrl+C) 或 Ctrl+Break 键会终止当前进程。默认值为 false,即终止当前进程。

由于您没有将其设置为true,因此您的应用程序将在事件处理程序完成运行后终止。根据当时发生的情况,似乎有时取消令牌有时间跳出 while 循环,而其他时候则没有:

试运行

您的原始代码可以修复如下:

public static void Main()
{
    using (var cancellationTokenSource = new CancellationTokenSource())
    {
        Console.CancelKeyPress += (_, ccea) => {
            cancellationTokenSource.Cancel();
            ccea.Cancel = true; //cancel the cancel.  There's too many cancels!
        };
        while (!cancellationTokenSource.Token.WaitHandle.WaitOne(1000))
        {
            Console.WriteLine("Still running...");
        }
        Console.WriteLine("Cancellation is requested. Trying to dispose cancellation token...");
    }
    Console.WriteLine("Just before close");
}
Run Code Online (Sandbox Code Playgroud)