Шаблон для реализации методов синхронизации в терминах непараллельной задачи (перевод / развертывание AggregateExceptions) - PullRequest
6 голосов
/ 13 января 2012

У меня есть метод Async, возвращающий задачу.

Я также хотел бы предложить синхронный эквивалент, но я не хочу, чтобы его потребителям приходилось распаковывать AggregateException с.

Теперь я понимаю, что вся идея в том, что вы не можете произвольно выбрать один общий способ, и я знаю, что мог бы пойти и прочитать множество других статей Стивена Тауба (я буду, но не сейчас), и я пойму это все и сам могу решить.

Тем временем я хочу использовать тот факт, что мои задачи на самом деле просто связаны между собой «рабочими процессами» без параллелизма, а просто с промежуточными ожиданиями (нет, не TPL DataFlow), которые не должны приводить к более чем одному исключению. В этом случае было бы целесообразно обрабатывать следующим образом:

    CallAsync().Wait();
}
catch( AggregateException ae)
{
    throw ae.Flatten().First()

или я гарантирую, что AggregateException всегда имеет InnerException, даже если их больше одного. Или есть случай, когда я должен вернуться к .Flatten().First()?


В некоторых документах TPL я вижу ссылку на метод Unwrap() в AggregateException (не уверен, было ли это расширением или что-то в бета-версии).

Как заполнитель я делаю:

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

Task CallAsync(){ ...

1 Ответ

21 голосов
/ 14 января 2012

Нет "чистого" способа сделать это, о котором я знаю.Вы не можете использовать throw someInnerException;, потому что вы потеряете стек везде, где возникло исключение в асинхронном рабочем процессе, и если вы просто используете throw;, вы, очевидно, будете распространять AggregateException.То, что вам нужно сделать для синхронного метода, это иметь какое-то исключение «оболочки», в которое вы можете вставить первое исключение из AggregateException и затем вывести его последовательно из синхронной версии метода.

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());
    }
}

FWIW, они решили это в 4.5 с помощью нового ExceptionDispatchInfo класса , который поможет вам распределять исключения между потоками, не разбивая стек.Тогда вы могли бы написать синхронную версию так:

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