为什么CancellationTokenSource挂起应用程序

时间:2018-03-28 14:21:04

标签: c# .net 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");
}

问题是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");
}

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

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

1 个答案:

答案 0 :(得分:3)

问题不在于您的取消令牌,而是您选择使用CancelKeyPress进行测试。发生此事件时,您将获得ConsoleCancelEventArgs,其中包含Cancel property

  

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

由于您未将其设置为true,因此应用程序在事件处理程序运行完毕后终止。根据当时正在发生的事情,似乎有时取消令牌有时间突破while循环,有时则不会:

test runs

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

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