由Stephen Toub阅读Task-based Asynchronous Pattern我试图了解取消对任务的影响。 在 Await 下的使用基于任务的异步模式部分中,在第3段中说:
如果任务或任务TResult>期待已在取消状态结束了 将抛出OperationCanceledException。
我试图在下面的代码中看到这一点。
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;
}
执行此代码不会抛出任何异常,只会打印:
价值任务的状态:已取消
现在,我的期望是DoStuffAsync()
方法,因为await Task.Delay(...)
被取消,我们等待任务以Canceled状态结束,因此应该抛出异常(根据TAP文档的引用),但是如果我在Console.ReadLine()
上设置断点并检查valueTask
它的状态是否已取消,并且例外为null
。
如果我误读了该文件,或者我提出的代码没有正确复制案例,有人可以帮助我理解吗?
答案 0 :(得分:3)
该方法返回任务本身,永远不会访问结果本身。如果您尝试访问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 Exception:
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);
}
因为没有要返回的任务对象可以包含状态,所以编译器必须警告执行代码它无法完全运行异步代码并抛出异常。
答案 1 :(得分:2)
当您尝试从此任务中获得结果时,您将获得OperationCanceledException
。
尝试在Main
方法的末尾添加此行:
valueTask.GetAwaiter().GetResult();
您将从TaskCanceledException
获得OperationCanceledException
,await
已取消的任务也将TaskCanceledException
投放。
您也可以使用valueTask.Result
和valueTask.Wait()
,但他们会将AggregateException
与单个成员TaskCanceledException
一起投放。
为了更好地了解await
中发生的事情,了解它如何在tryroslyn转换为状态机可能会有用。
答案 2 :(得分:0)
修改:此答案适用于您希望使用TaskCanceledException
内部Task
的情况,但您的问题是关于将其置于其外部但是它可能仍然对某些人有用。 ; - )
在Task
(DoAsyncStuff
函数)中,您必须检查CancellationToken
,然后自行抛出TaskCanceledException
或者只需拨打ThrowIfCancellationRequested()
即可。