2

我有以下代码运行而不会引发异常:

        var t = Task.Factory.StartNew(() => LongRunningMethod(cancellationToken, progress), cancellationToken);
        t.ContinueWith(Callback, TaskScheduler.FromCurrentSynchronizationContext());

在“LongRunningMethod”中,我调用了cancellationToken.ThrowIfCancellationRequested()。回调将始终被调用(这是我想要的),并且正确传递给回调的任务将 IsCancelled 设置为 true 或 false。

使用 async/await 关键字,我必须将上述行修改为以下内容:

        try
        {
            await Task.Factory.StartNew(() => LongRunningMethod(cancellationToken, progress), cancellationToken);
            textEdit1.Text =  "Done";
        }
        catch (OperationCanceledException)
        {
            textEdit1.Text = "Cancelled";
        }

在这种情况下,为什么 ThrowIfCancellationRequested() 会引发我需要捕获的实际异常?

4

2 回答 2

2

使用ContinueWith,您将获得Task先前运行的,您可以询问它是否取消(Task.IsCancelled)。,await你没有那个。与取消通信的唯一方法是通过异常。

现在,await只是使用任务,所以你可以“插入”一个延续。例如:

await Task.Factory
  .StartNew(() => LongRunningMethod(cancellationToken, progress), cancellationToken)
  .ContinueWith(t=>Trace.WriteLine("Canceled"), TaskContinuationOptions.OnlyOnCanceled);

您仍然可以使用 await 然后使用ContinueWith处理取消场景。因此,从技术上讲,正在等待继续。await

于 2012-08-16T13:35:30.103 回答
2

async旨在使异步代码更容易,并尽可能像等效的同步代码一样。

首先,请注意总是会抛出异常。如果已请求取消,ThrowIfCancellationRequested将(惊喜)抛出异常。

在您现有的代码中,会捕获此异常,然后将其放置在Task(包装在 中AggregateException)。将Task这种情况解释为“已取消”。然后你可以在你的延续中检查布尔标志。

但考虑等效的同步代码:

try
{
  LongRunningMethod(cancellationToken, progress);
}
catch (OperationCanceledException)
{
}

这看起来很像这种async方法。即使您使用ContinueWith,仍然有一个异常被抛出并被捕获 - 从逻辑上讲,您正在执行try/ catch。就个人而言,我更喜欢显式try/catch因为:

  • 意图更清晰,因此代码更易于阅读和维护。
  • 代码更易于访问(绝大多数 C# 程序员理解try/ catch;相对少数人理解ContinueWith)。

但是,ContinueWith(稍微)更有效。

于 2012-08-16T19:08:26.947 回答