阅读 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/