Как использовать ConfigureAwait на асинхронных методах - PullRequest
2 голосов
/ 03 июля 2019

Я ищу правильные варианты использования ConfigureAwait Я определил действительный вариант использования для ConfigureAwait (то есть после того, как ожидание не требует контекста синхронизации вызывающего потока) и не смог найти никаких советов по использованию ConfigureAwait васинхронные методы

Мой код выглядит следующим образом

Public async Task StartMyProgram()
{
     await RunBackgroundTask();
}

Private async Task RunBackgroundTask()
{
     await Task.Delay(5000);
}

Для правильного использования ConfigureAwait я предполагаю, что это должно использоваться при обоих вызовах await, как показано ниже:

Public async Task StartMyProgram()
{
     await RunBackgroundTask().ConfigureAwait(false);
}

Private async Task RunBackgroundTask()
{
     await Task.Delay(5000).ConfigureAwait(false);
}

или мне просто нужно это по приватному RunBackgroundTask методу?

Ответы [ 3 ]

2 голосов
/ 03 июля 2019

Это упрощение, но вы можете предположить, что ConfigureAwait(false) - это тонкий способ сказать «эй, материал, который вы собираетесь вызвать, не будет захватывать текущий контекст синхронизации».

Ключевое слово здесь current: контекст синхронизации используется для синхронизации с конечным автоматом асинхронного режима. Ваши асинхронные методы превращаются в задачи, и вся последовательность должна возвращаться только тогда, когда все выполнено, как вы просили. Для выполнения такой синхронизации внутреннему планировщику задач требуется контекст синхронизации. Когда вы пишете библиотеку, вы понятия не имеете, что делает вызывающая программа, и, в частности, теперь вы знаете о дополнительных потоках, которые могут запускать асинхронные методы (например, одновременные асинхронные методы в разных потоках или насосы сообщений). По этой причине вы играете безопасно, вызывая ConfigureAwait(false), указывая среде выполнения не заимствовать (и перехватывать) контекст синхронизации вызывающей стороны, а использовать новый.

Зачем ты это делаешь? Во-первых, потому что брать что-то в недетерминированном состоянии нехорошо. Но что более важно, чтобы избежать взаимоблокировок: фактически, во время выполнения вашего асинхронного метода вы по умолчанию используете захваченный контекст вызывающей стороны. Это означает, что вы можете оказаться в тупиках и / или тонких проблемах, потому что поток, необходимый для выполнения задачи, может зависнуть вашим методом, что приведет к тупику.

По умолчанию, когда вы используете async / await, он возобновляется в исходном потоке, который запустил запрос. Однако, если другой длительный процесс в настоящее время занял этот поток, вы застрянете, ожидая его завершения. Чтобы избежать этой проблемы, вы можете использовать метод ConfigureAwait с ложным параметром. Когда вы это делаете, это говорит Задаче, что она может возобновить себя в любом доступном потоке, вместо того, чтобы ожидать поток, который его первоначально создал. Это ускорит ответы и позволит избежать многих тупиков.

При ConfigureAwait(true) (по умолчанию), когда вы возобновляете работу в другом потоке, контекст синхронизации потока теряется, что приводит к потере языковых и / или языковых настроек наряду с другими вещами, такими как HttpContext.Current (это происходит в .NET Standard) .

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

2 голосов
/ 03 июля 2019

или мне просто нужно это для частного метода RunBackgroundTask?

Каждый метод должен сам принять решение ConfigureAwait(false). Это потому, что каждый метод захватывает свой собственный контекст на await, независимо от того, что делают его вызывающие / вызываемые методы. ConfigureAwait настраивает один await; он вообще не «течет».

Итак, RunBackgroundTask нужно определить, "мне нужно возобновить в моем контексте?" Если нет, тогда следует использовать ConfigureAwait(false).

И StartMyProgram необходимо определить, "мне нужно возобновить в моем контексте?" Если нет, тогда следует использовать ConfigureAwait(false).

0 голосов
/ 03 июля 2019

При вводе RunBackgroundTask вы не понимаете, что такое SynchronizationContext . Так что вам действительно не нужно захватывать его и продолжать использовать .ConfigureAwait(false).

...