任务未按预期取消

Task not Cancelling as expected

我们得到了以下场景:

class Program
{
    static void Main(string[] args)
    {
        // trigger the delayed function
        trigger();

        // cancel the running task.
        _token.Cancel();

        // keep window open ;-)
        Console.ReadLine();

    }

    private static CancellationTokenSource _token = null;
    private static async void trigger()
    {
        _token = new CancellationTokenSource();

        try
        {
            // run task
            await Task.Run(async () =>
            {
                // wait time
                await Task.Delay(2500);

                // we should be cancelled here !!
                Console.WriteLine(string.Format("IsCancellationRequested={0}", _token.Token.IsCancellationRequested));
                Console.WriteLine("SHOULD NOT HAPPEN");

            }, _token.Token);
        }
        catch (TaskCanceledException)
        {
        }
    }
}

IMO 预期的行为是任务的操作主要在 Task.Delay(2500) 处理后被取消。

但是控制台正在打印:

IsCancellationRequested=True
SHOULD NOT HAPPEN

这感觉像是一个错误。如果您将 CancellationToken 作为参数添加到 Task.Delay-Function,它将按预期工作。

如果任务中的函数使用Task.Delay,那么如何处理取消,你可能不知道?

.Net 中的取消是合作的。将令牌作为参数传递给 Task.Run 只会将令牌与返回的任务相关联。

要真正取消任务,您需要检查任务本身内的令牌。如果您希望在延迟内取消任务,您需要将令牌传递给 Task.Delay 方法。否则你只能在延迟之前或之后检查 while:

await Task.Run(async () =>
{
    _token.Token.ThrowIfCancelltionRequested();
    await Task.Delay(2500, _token.Token);
    _token.Token.ThrowIfCancelltionRequested();

    Console.WriteLine(string.Format("IsCancellationRequested={0}", _token.Token.IsCancellationRequested));
    Console.WriteLine("SHOULD NOT HAPPEN");

}, _token.Token);

这是一篇不错的 MSDN 文章:https://msdn.microsoft.com/en-us/library/dd537607(v=vs.110).aspx

The calling thread does not forcibly end the task; it only signals that cancellation is requested. If the task is already running, it is up to the user delegate to notice the request and respond appropriately. If cancellation is requested before the task runs, then the user delegate is never executed and the task object transitions into the Canceled state.

正如 i3arnon 所建议的那样,您可以在执行实际工作之前检查状态并抛出异常。文章代码摘录:

...
  // Was cancellation already requested?  
      if (ct.IsCancellationRequested == true) {
         Console.WriteLine("Task {0} was cancelled before it got started.",
                           taskNum);
         ct.ThrowIfCancellationRequested();
      } 
...