【问题标题】:ThrowIfCancellationRequested with and without async/await?ThrowIfCancellationRequested 有和没有异步/等待?
【发布时间】:2012-08-12 20:16:41
【问题描述】:

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

        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() 会引发我需要捕获的实际异常?

【问题讨论】:

    标签: c# asynchronous task .net-4.5


    【解决方案1】:

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

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

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

    但考虑等效的同步代码:

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

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

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

    但是,ContinueWith (稍微)效率更高。

    【讨论】:

      【解决方案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 正在等待继续。

      【讨论】:

      • 有道理,谢谢。但是,这会导致以下问题 - 'Task.Factory.StartNew' 返回一个任务,那么为什么 'await Task.Factory.StartNew' 不返回一个我可以检查其取消状态的任务?
      • 可能是为了保持语法相当干净。 async/await 也不是异步语法的全部。它适合异步编程的特定领域:主要是为了让 UI 线程保持长时间的操作,以保持 UI 线程的响应。但是,我已经更新了答案以显示awaitTask 的混合体
      • 现在,我发现使用 ThrowIsCancellationIsRequested 会引发异常,而不是在 VS 2012 中调用延续... FWIW
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-15
      • 2015-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-17
      相关资源
      最近更新 更多