捕获异步操作的异常

本文关键字:异常 异步操作 | 更新日期: 2023-09-27 18:20:16

我在这里阅读了更多关于异步的信息: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);

捕获异步操作的异常

第一个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;
        }
    }
}

显然,等待任务更简单,因此建议使用此方法从任务中检索结果

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

他肯定会用另一种方法吗。这只是代码作者喜欢的方法,因为它允许他编写更像传统同步代码的代码,而不是从根本上改变代码的风格。

你说得对。没有必要。你可以用代替它

if (recommendation.Result) 
    BuyStock(symbol);

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

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

如果我已经这样做了:任务推荐=等待任务。WhenAny(推荐);为什么这样做:如果(等待推荐)买入股票(符号);

因为Task.WhenAny返回一个Task<Task<bool>>,并且您希望打开输出Task以检索结果布尔。您可以通过访问返回的TaskTask.Result属性来执行同样的操作

其他答案指出,您必须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];
    }
}

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