Должен ли я использовать configure await во всех методах или только в первом методе? - PullRequest
0 голосов
/ 25 мая 2019

У меня есть библиотека с асинхронными методами, и я прочитал, что для библиотек рекомендуется использовать ConfigureAwait(false).

Например, если у меня есть что-то вроде:

public async Task myMethod01()
{
    await myMethod02();
}

private async Task myMethod02()
{
    await myMethod03();
}

private async Task myMethod03()
{
    await anotherMetodAsync().ConfigureAwait(false);
}

Пользователь библиотеки может использовать только method01, остальные 2 метода являются закрытыми, поскольку они являются вспомогательными методами для основного метода method01().

Но я не знаю, нужно ли использовать ConfigureAwait только в первом методе цепного вызова или мне следует использовать во всех них.

Ответы [ 2 ]

1 голос
/ 26 мая 2019

ТЛ; др

Да , это необходимо для того, чтобы все ваши асинхронные продолжения в коде библиотеки выполнялись в потоке пула потоков (в зависимости от SynchronizationContext / TaskScheduler используется).

Хотите узнать больше?

Task.ConfigureAwait (Boolean)

  • true пытается маршалировать остаток от асинхронного метода обратно в исходный захваченный контекст
  • false расписания остаток от асинхронного метода в потоке пула потоков

Рассмотрим следующий пример WPF: WPF использует DispatcherSynchronizationContext для возобновления асинхронных продолжений в контексте пользовательского интерфейса, поскольку фоновый поток не может обновить содержимое элементов управления.

private async void Button_Click(object sender, RoutedEventArgs e)
{
    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context

    await CompleteAsynchronously();

    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context

    await CompleteAsynchronously().ConfigureAwait(false);

    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //true
}

private async Task CompleteAsynchronously()
{
    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context

    await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false);

    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //true
}

Здесь вы видите, что флаг continueOnCapturedContext вызванного метода не влияет на вызывающего. Тем не менее, вызываемый метод выполняется (или, по крайней мере, начинает выполняться) в потоке, в котором запущен вызывающий объект, конечно.

Однако захват текущего контекста (либо текущего SynchronizationContext; если ноль, то текущего TaskScheduler) происходит только тогда, когда ожидается незавершенное задание. Если задание завершается синхронно, continueOnCapturedContext не имеет никакого эффекта, а остальная часть метода продолжает работать синхронно в текущем потоке.

private async void Button_Click(object sender, RoutedEventArgs e)
{
    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context

    await CompleteSynchronously().ConfigureAwait(false);

    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context
}

private async Task CompleteSynchronously()
{
    await Task.Delay(0);
}

Таким образом, в коде вашей библиотеки (при условии, что вам никогда не требуется контекст), вы всегда должны использовать ConfigureAwait(false), чтобы гарантировать, что для асинхронных продолжений не захватывается контекст, независимо от того, как платформа вызывает ваши сборки (например, WPF, ASP). .NET Core, консоль, ...).

Подробнее см. Рекомендации по асинхронному программированию (ia ConfigureAwait) в этой статье MSDN Magazine от Stephen Cleary .

1 голос
/ 25 мая 2019

Вы должны использовать ConfigureAwait(false) во всех асинхронных вызовах. Если этого не сделать, первый асинхронный вызов (без ConfigureAwait(false)) примет SynchronizationContext, и это может привести к взаимоблокировке в определенных условиях (например, в ASP.NET) при синхронном ожидании этого вызова.

Мой совет - прочитать эту статью , написанную Стивеном Клири. Интересующая вас часть:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...