异步方法,取消和任务状态

时间:2018-07-06 11:54:39

标签: c# .net async-await cancellation

给出类似方法的签名

Taks<int> ComputeAsync(..., CancellationToken cancellationToken)

,期望返回的任务完成

  • RanToCompletion(和Result集)或
  • Faulted(和Exception集)或
  • Canceled,如果cancellationToken请求取消

当将该方法实现为async方法时,如何实现?

通过测试,我发现从async方法中抛出OperationCanceledException显然可以完成处于Canceled状态的任务(无论异常中包装了什么令牌,无论该令牌是否IsCancellationRequested):

var tasks = new[]
{
    Task.FromResult(42),
    Task.FromException<int>(new Exception("Boom!")),
    Task.FromCanceled<int>(new CancellationToken(true)),
    Task.FromException<int>(new OperationCanceledException(new CancellationToken(true)))
};

async Task<int> Await(Task<int> task) => await task;

foreach (var t in tasks)
    Console.WriteLine($"{t.Status} - {Await(t).Status}");

输出:

RanToCompletion - RanToCompletion
Faulted - Faulted
Canceled - Canceled
Faulted - Canceled

但是,我似乎找不到任何有关上述行为的文档或其他信息。可以跨框架版本依赖它吗?

如果答案是肯定的,那么cancellationToken.ThrowIfCancellationRequested()会做正确的事,但是如果没有抓住OperationCanceledException(可能来自await个任务),则可以设置任务Canceled尽管!cancellationToken.IsCancellationRequested。因此,为了获得可预测和正确的结果,我们是否需要将每个可取消的异步方法包装到try / catch中,以确保没有抛出OCE,除非它在正确的CancellationToken上,或者是否有更优雅的方法? / p>

如果答案是否定的,那么我们是否必须退回到TaskCompletionSource的东西上?

1 个答案:

答案 0 :(得分:0)

从您的示例开始-尚不清楚您使用cancelToken的意图。因此,我将不得不从我的用法中谈一谈。我们使用Windows服务,需要知道是否已使用OnStop事件。 TokenSource对象具有Cancel方法,以向Task发出信号,告知我们需要停止操作。令牌布尔值指示请求取消。通过抛出OperationCancelled-您可以捕获该值并优雅地处理它。都是以控制任务的同步的名义。如果这样做,您的方案将在某种程度上具有相同的需求。 附加说明7/16/2018:

您在问题中喊出签名(在Task上正确拼写)

Task<int> ComputeAsync(..., CancellationToken cancellationToken)

但是您的示例-构建了一个在所有任务中都不使用该签名的任务数组。您的示例太不完整,无法给出答案。 (似乎您不了解任务的签名)。当您使用取消令牌调用任务时,您的任务动作就是签名示例中的省略号-并且您没有显示要传递给任务的内容。

您使用的Task.FromResult表示-返回此结果。

如果在'FromResult'方法上使用Visual Studio F12,您将看到带有此注释的源代码,指出该状态

Summary:
    //     Creates a System.Threading.Tasks.Task`1 that's completed 
successfully with the
    //     specified result.

这完全与您的问题不符。 这是FromResult用于什么的stackoverflow答案 What is the use for Task.FromResult<TResult> in C#