Лучшая практика библиотеки Async: ConfigureAwait (false) и настройка контекста синхронизации

Хорошо известно, что в библиотеке общего назначения ConfigureAwait(false) следует использовать для каждого вызова ожидания, чтобы избежать продолжения текущего SynchronizationContext.

В качестве альтернативы переполнению всей базы кода с помощью ConfigureAwait(false) можно просто установить значение SynchronizationContext равным null один раз, методом общедоступной поверхности и восстановить его перед возвратом пользователю. Другими словами:

public async Task SomeSurfaceMethod()
{
    var callerSyncCtx = SynchronizationContext.Current;
    SynchronizationContext.SetSynchronizationContext(null);
    try
    {
        // Do work
    }
    finally
    {
        SynchronizationContext.SetSynchronizationContext(callerSyncCtx);
    }
}

Это также можно обернуть в using для лучшей читаемости.

Есть ли недостаток этого подхода, не приводит ли он к такому же эффекту?

Основным преимуществом является, очевидно, читаемость - удаление всех вызовов ConfigureAwait(false). Это также может снизить вероятность того, что вы забудете ConfigureAwait(false) где-нибудь (хотя анализаторы смягчают это, и можно утверждать, что разработчики могли бы также забыть об изменении SynchronizationContext).

Несколько экзотическое преимущество - это не включение выбора захвата SynchronizationContext или не во всех методах. Другими словами, в одном случае я могу запустить метод X с SynchronizationContext, в то время как в другом я могу запустить один и тот же метод без него. Когда ConfigureAwait(false) внедряется везде, что невозможно. Конечно, это довольно редкое требование, но я столкнулся с ним во время работы над Npgsql (вызвав этот вопрос).

Ответы

Ответ 1

Как писал @MrinalKamboj в комментариях, здесь был предложен <временный параметр SynchronizationContext для null в методе общественной поверхности и отладки здесь. Кажется, что нет какой-либо конкретной проблемы, связанной с этим (см. Стивен Клири здесь).