根据非并行任务(转换/解包AggregateExceptions)实现同步方法的模式

Rub*_*ink 6 asynchronous exception-handling task-parallel-library c#-4.0

我有一个返回任务的Async方法.

我也希望提供同步等效,但我不希望它的消费者必须去解包AggregateException.

现在我明白了整个想法是你不能随意选择一个,我知道我可以阅读更多的Stephen Toub文章(我会,但不是现在),我会理解这一切,并且可以自己决定.

在此期间,我想使用这样一个事实:我的任务实际上只是没有并行性的链接"工作流程",只是干预Waits(不,不是TPL DataFlow),这不应该导致多个异常.在这种情况下,处理如下是适当的:

    CallAsync().Wait();
}
catch( AggregateException ae)
{
    throw ae.Flatten().First()
Run Code Online (Sandbox Code Playgroud)

或者我保证即使有不止一个也AggregateException总是有InnerException.还是有一种情况我应该回归.Flatten().First()


在一些TPL文档中,我看到了对Unwrap()方法的引用AggregateException(不确定它是否是扩展或beta版本中的某些内容).

作为占位符,我正在做:

void Call( )
{
    try
    {
        CallAsync().Wait();
    }
    catch ( AggregateException ex )
    {
        var translated = ex.InnerException ?? ex.Flatten().InnerExceptions.First();
        if ( translated == null )
            throw;
        throw translated;                 }
}

Task CallAsync(){ ...
Run Code Online (Sandbox Code Playgroud)

Dre*_*rsh 21

我知道,没有"干净"的方法来做到这一点.你不能使用,throw someInnerException;因为你会在异步工作流中的异常发生地丢失堆栈,如果你只是使用throw;你显然会传播它AggregateException.你需要为同步方法做些什么是有一些"包装"异常,你可以填充AggregateExceptioninto 的第一个异常,然后从方法的同步版本中一致地抛出它.

void Call()
{
    try
    {
        CallAsync().Wait();
    }
    catch (AggregateException ex)
    {
        throw new MyConsistentWrapperException("An exception occurred while executing my workflow. Check the inner exception for more details.", ex.Flatten().InnerExceptions.First());
    }
}
Run Code Online (Sandbox Code Playgroud)

FWIW,他们已经在4.5中使用ExceptionDispatchInfo解决了这个问题,它将帮助您跨线程编组异常而不会破坏堆栈.然后你可以写这样的同步版本:

void Call()
{
    try
    {
        CallAsync().Wait();
    }
    catch (AggregateException ex)
    {
        ExceptionDispatchInfo.Capture(ex.Flatten().InnerExceptions.First()).Throw();
    }
}
Run Code Online (Sandbox Code Playgroud)