CancellationTokenSource的行为不符合预期

Taw*_*feh 5 .net c# multithreading task-parallel-library cancellation-token

在这种情况下预期的是,如果用户通过按Enter键取消任务,则挂起的另一个任务ContinueWith将运行,但事实并非如此,AggregateException尽管显式处理ContinueWith显然没有被执行,但仍保持抛出.
请问下面的任何澄清?

class Program
{
    static void Main(string[] args)
    {
        CancellationTokenSource tokensource = new CancellationTokenSource();
        CancellationToken token = tokensource.Token;

        Task task = Task.Run(() =>
        {
            while (!token.IsCancellationRequested)
            {
                Console.Write("*");
                Thread.Sleep(1000);
            }
        }, token).ContinueWith((t) =>
        {
            t.Exception.Handle((e) => true);
            Console.WriteLine("You have canceled the task");
        }, TaskContinuationOptions.OnlyOnCanceled);

        Console.WriteLine("Press any key to cancel");
        Console.ReadLine();
        tokensource.Cancel();
        task.Wait();
    }
}
Run Code Online (Sandbox Code Playgroud)

i3a*_*non 7

让我们从一些事实开始:

  1. 当您传递a CancellationToken作为参数时,Task.Run只有在任务开始运行之前取消它才有效.如果任务已在运行,则不会取消.
  2. 要在任务开始运行取消任务,您需要使用CancellationToken.ThrowIfCancellationRequested,而不是CancellationToken.IsCancellationRequested.
  3. 如果任务被取消,其Exception属性不会包含任何异常null.
  4. 如果继续任务由于某种原因未运行,则表示已取消.
  5. 任务包含来自其自身及其所有子任务的异常(因此AggregateException).

这就是你的代码中发生的事情:

任务开始运行,因为令牌未被取消.它将一直运行,直到令牌被取消.在它结束之后,延续将不会运行,因为它仅在前一个任务被取消时运行,而它还没有运行.当你Wait执行任务时,它将抛出AggregateException一个TaskCanceledException因为延续被取消(如果你将删除该延续,则异常将消失).

解:

您需要修复任务以使其实际被取消,并删除(或空检查)异常处理,因为没有异常:

var task = Task.Run(new Action(() =>
{
    while (true)
    {
        token.ThrowIfCancellationRequested();
        Console.Write("*");
        Thread.Sleep(1000);
    }
}), token).ContinueWith(
    t => Console.WriteLine("You have canceled the task"),
    TaskContinuationOptions.OnlyOnCanceled);
Run Code Online (Sandbox Code Playgroud)