【问题标题】:Capturing Exceptions on async operations捕获异步操作的异常
【发布时间】:2014-09-13 23:38:07
【问题描述】:

我在这里阅读更多关于异步的内容:http://msdn.microsoft.com/en-us/library/hh873173(v=vs.110).aspx

通过这个例子:

Task<bool> [] recommendations = …;
while(recommendations.Count > 0)
{ 
    Task<bool> recommendation = await Task.WhenAny(recommendations);    
    try
    {
        if (await recommendation) BuyStock(symbol);
        break;
    }
    catch(WebException exc)
    {
        recommendations.Remove(recommendation);
    }
}

我想知道,如果我已经在 Task.WhenAny 上执行 await,为什么我需要在 try 块内再次 await?

如果我已经这样做了:Task&lt;bool&gt; recommendation = await Task.WhenAny(recommendations); 为什么这样做:if (await recommendation) BuyStock(symbol);

【问题讨论】:

    标签: c# .net task-parallel-library async-await


    【解决方案1】:

    第一个await 存在异步等待第一个任务完成(即recommendation)。 第二个await 只是为了从已经完成的任务中提取实际结果,并抛出存储在任务中的异常。 (请务必记住,等待完成的任务是经过优化的,并且会同步执行)。

    获得结果的另一个选项是使用Task&lt;T&gt;.Result,但是它处理异常的方式不同。 await 会抛出实际异常(例如WebException),而Task&lt;T&gt;.Result 会抛出包含实际异常的AggregateException

    Task<bool> [] recommendations = …;
    while(recommendations.Count > 0)
    { 
        Task<bool> recommendation = await Task.WhenAny(recommendations);    
        try
        {
            if (recommendation.Result) 
            {
                BuyStock(symbol);
            }
            break;
        }
        catch(AggregateException exc)
        {
            exc = exc.Flatten();
            if (exc.InnerExceptions[0] is WebException)
            {
                recommendations.Remove(recommendation);
            }
            else
            {
                throw;
            }
        }
    }
    

    显然等待任务更简单,因此这是从任务中检索结果的推荐方式。

    【讨论】:

    • 如果TaskWebException 而出错,这将抛出AggregateException,而不是WebException
    • @Servy 澄清了这一点。
    • 您现在已经提到存在差异,但是由于您的更改,您仍然破坏了代码,因为您以代码不支持的方式更改了错误处理语义.您的答案的症结在于,您可以更改代码以使用Result,这本质上是错误的,因为通过进行更改,您破坏了代码。这段代码的作者在这里使用await 有一个非常好的理由
    • @Servy 我不建议使用该代码,它是“为什么使用await?”答案的一部分。我也澄清了这一点。
    • 您声称它给出了完全相同的行为,但事实并非如此,因为您破坏了代码。现在你的答案剩下的就是你错误答案的残余,据你自己承认,这显然根本没有用。
    【解决方案2】:

    在此处使用await 创建了所需的错误处理语义。如果他使用Result 而不是await 那么AggregateException 将被直接重新抛出;当使用await 时,AggregateException 中的第一个异常被拉出, 异常被重新抛出。明确此代码的作者希望抛出 WebException,而不是需要手动解包的 AggregateException

    他可以使用其他方法吗,当然。这只是代码作者更喜欢的方法,因为它允许他编写更像传统同步代码的代码,而不是从根本上改变代码的样式。

    【讨论】:

      【解决方案3】:

      你是对的。没有必要。您可以将其替换为

      if (recommendation.Result) 
          BuyStock(symbol);
      

      还要注意await 在完成任务时不会等待(不会设置继续)。在这种情况下,它只会同步执行作为优化。我猜作者利用了这种优化。

      如果你问作者为什么这样写,可能是一致性?只有他自己知道!

      【讨论】:

        【解决方案4】:

        如果我已经这样做了:任务推荐 = await Task.WhenAny(recommendations);为什么这样做:如果(等待推荐)BuyStock(symbol);

        因为Task.WhenAny 返回一个Task&lt;Task&lt;bool&gt;&gt;,并且您想解开外部Task 以检索结果布尔值。您可以通过访问返回的TaskTask.Result 属性来做同样的事情

        【讨论】:

          【解决方案5】:

          其他答案指出,您必须 await await Task.WhenAll 返回的任务才能解开返回值(或者,您可以使用 Result 属性)。

          但是,您也可以摆脱 try/catch(避免捕获不必要的异常是一件好事)

          Task<bool> recommendation = await Task.WhenAny(recommendations);    
          if(!recommendation.IsFaulted)
          {
              if (await recommendation) BuyStock(symbol);
              break;
          }
          else
          {
              if(recommendation.Exception.InnerExceptions[0] is WebException)
              {
                  recommendations.Remove(recommendation);
              }
              else
              {
                  throw recommendation.Exception.InnerExceptions[0];
              }
          }
          

          【讨论】:

          • 你确定任务出错时WhenAny不会抛出异常吗?
          • @SriramSakthivel:完全确定。 WhenAny返回一个任务,其结果是第一个完成的任务。如果任务成功或失败,则任务完成。如果任务出错,WhenAnysucceeds 会为您提供第一个完成的任务(即出错的任务)。
          • recommendation.Exception 不会包含AggregateException
          • @I3arnon:你是对的。我已经相应地更新了我的答案。
          【解决方案6】:

          因为Task.WhenAny&lt;TResult&gt;(IEnumerable&lt;Task&lt;TResult&gt;&gt; tasks) 返回一个Task&lt;Task&lt;TResult&gt;&gt;。外部任务(由 Task.WhenAny 调用创建的任务)将在传递给它的任何任务完成时完成,结果是已完成的任务。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-03-23
            • 2011-08-12
            • 2013-04-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-10-09
            • 1970-01-01
            相关资源
            最近更新 更多