c# - 等待的任务以取消状态结束不会抛出

标签 c# asynchronous task-parallel-library

阅读 Task-based Asynchronous Pattern作者:Stephen Toub 我正在尝试了解取消对任务的影响。 在 Await 下的 Consuming the Task-based Asynchronous Pattern 部分的第 3 段中说:

If the Task or Task TResult> awaited ended in the Canceled state, an OperationCanceledException will be thrown.

我试图在下面的代码中看到这一点。

static void Main(string[] args)
{
   CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
   CancellationToken cancellationToken = cancellationTokenSource.Token;

   Task<int> valueTask = DoStuffAsync(cancellationToken);
   Thread.Sleep(TimeSpan.FromSeconds(1));
   cancellationTokenSource.Cancel();

   Console.WriteLine("value task's status: {0}", valueTask.Status); 
   Console.ReadLine();
}

DoStuffAsync() 方法

static async Task<int> DoStuffAsync(CancellationToken cancellationToken)
{
    await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
    return 42;
}

执行这段代码不会抛出任何异常,它只是打印:

value task's status: Cancelled

现在,我的期望是在 DoStuffAsync() 方法中,因为 await Task.Delay(...) 被取消,我们等待任务以 Canceled 状态结束,因此应该抛出异常(根据 TAP 文档的引用),但如果我在 Console.ReadLine() 上放置断点并检查 valueTask,它的状态为 Cancelled,并且异常为 null

如果我误读了文档,或者我想出的代码没有正确重现案例,谁能帮助我理解?

最佳答案

该方法返回任务本身,永远不会访问结果本身。如果您尝试访问 valueTask.Result,您将得到一个 TaskCanceledException(在 AggregateException 中)。

同样,如果您要await valueTask(Main 必须通过异步),您将尝试获取结果,在这种情况下也会抛出异常。这是上述段落中描述的行为。

线索是 Task 对象是有效的,但结果不是,因为如果任务被取消,await 之后的代码永远不会执行。例如:

static async Task<int> DoStuffAsync(CancellationToken cancellationToken)
{
    var delay = Task.Delay(5000, cancellationToken);
    Console.WriteLine("before await"); 
    await delay;
    Console.WriteLine("after await"); 
    return 42;
}

如果任务被取消,则第二个写入行永远不会执行。 只要未访问 DoStuffAsync 返回的任务的结果,该任务就是一个有效对象,只是被取消了。访问结果将强制运行时确认任务从未完成并抛出异常。

如果异步方法不返回任务,您还会得到一个 TaskCancelled 异常:

static async void Main()
{
   CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
   CancellationToken cancellationToken = cancellationTokenSource.Token;

   DoStuffAsync(cancellationToken);
   Thread.Sleep(TimeSpan.FromSeconds(1));
   cancellationTokenSource.Cancel();
   Console.ReadLine();
}

// Define other methods and classes here
static async void DoStuffAsync(CancellationToken cancellationToken)
{
    await Task.Delay(5000, cancellationToken);
}

因为没有要返回的任务对象可以包含状态,编译器不得不警告执行代码它不能完全运行异步代码并抛出异常。

关于c# - 等待的任务以取消状态结束不会抛出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40765495/

相关文章:

asynchronous - 相当于Dart的TypeScript `Completer`

c# - 是否建议将 prevTask.Wait() 与 ContinueWith(来自任务库)一起使用?

c# - 如何清空 BlockingCollection

c# - 如何将列表项绑定(bind)到gridview

c# - Unity 上的虚线弹跳

C# - Thread.Join(Milliseconds) 和 finally block

javascript - 异步/等待语法错误 : await is only valid in async functions and the top level bodies of modules

c# - 可访问性不一致 : Alternative to having a public instance of a private nested class

C# 应用程序应该是异步的,但 UI 仍然卡住

c# - 可以立即终止的任务等价物