捕获异步操作的异常

时间:2014-09-02 13:14:16

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

我正在阅读有关异步的更多内容: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执行等待,为什么我需要在try块内再次等待?

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

6 个答案:

答案 0 :(得分:8)

第一个await存在异步等待第一个任务完成(即recommendation)。 第二个await仅用于从已完成的任务中提取实际结果,并抛出存储在任务中的异常。 (重要的是要记住等待已完成的任务已经过优化并将同步执行)。

获取结果的另一个选项是使用Task<T>.Result,但它处理异常的方式不同。 await会抛出实际的异常(例如WebException),而Task<T>.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;
        }
    }
}

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

答案 1 :(得分:5)

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

他确实可以使用其他方法吗?这只是代码作者首选的方法,因为它允许他更像传统的同步代码编写代码,而不是从根本上改变代码的风格。

答案 2 :(得分:4)

你是对的。没有必要。您可以用

替换它
if (recommendation.Result) 
    BuyStock(symbol);

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

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

答案 3 :(得分:1)

  

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

因为Task.WhenAny返回Task<Task<bool>>,并且您想要解包外部Task以检索生成的bool。您可以通过访问返回的Task.Result

Task属性来执行相同操作

答案 4 :(得分:1)

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

但是,你也可以摆脱你的尝试/捕获(这是一件好事,以避免捕获不必要的异常)

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];
    }
}

答案 5 :(得分:0)

因为Task.WhenAny<TResult>(IEnumerable<Task<TResult>> tasks)会返回Task<Task<TResult>>。外部任务(由Task.WhenAny调用创建的任务)将在传递给它的任何任务完成时完成,结果是完成的任务。