Является ли ConfigureAwait(false) главным образом для задней границы?

#c# #asynchronous

#c# #асинхронный

Вопрос:

Отредактированное примечание: Не считайте мой вопрос и предыдущее понимание правильными, ответы проясняют, где были мои заблуждения.

Учитывая этот стек вызовов асинхронных методов:

 InterfaceMethod > InternalPublicMethod > InternalPrivateMethod > HttpWebRequest.GetResponseAsync()
  

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

Если мой первый InterfacedMethod вызывает await InternalPublicMethod().ConfigureAwait(false) , то InternalPublicMethod выполняется независимо от контекста, верно? Итак, когда он вызывает InternalPrivate , который затем вызывает HttpWebRequest.GetResponseAsync() , разве они также не выполняются независимо от контекста? Я не хочу использовать ConfigureAwait(false) , чтобы «быть в безопасности». Я хочу делать то, что необходимо и правильно. Я не верю, что SynchronizationContext повторно присоединяется к асинхронному стеку после выполнения предыдущего вызова в стеке ConfigureAwait(false)

Второй вопрос: если потребитель моей библиотеки вызывает один из моих интерфейсных методов, должны ли они вызывать ConfigureAwait(false) ? Если вы не знаете или не доверяете сторонней библиотеке, возможно, вам будет лучше сделать это, чтобы «быть в безопасности». Но если это библиотека, которую я использую, и я знаю, что все ее интерфейсные методы и внутренний код используют ConfigureAwait(false) , мне не нужно при их вызове, верно?

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

В настоящее время у меня нет никаких проблем с тем, как я это делаю, я просто хочу нести ответственность за эту библиотеку, которую я опубликовал.

Ответ №1:

тогда InternalPublicMethod выполняется независимо от контекста, верно?

Нет. Вы настраиваете ожидание, а не вызываемый метод. InternalPublicMethod выполняется в текущем контексте синхронизации. Это код, который выполняется после его ожидания, который может больше не выполняться в текущем контексте синхронизации.

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

Вы только влияете на продолжение в текущем методе. Здесь вы не влияете на то, что находится выше или ниже вас в стеке вызовов. await вызовы выше вас в стеке вызовов могут сами захватить стек вызовов и могут использовать его для своих продолжений. Продолжения, созданные в InternalPublicMethod , могут захватывать стек вызовов и использовать его для своих продолжений. Вы только влияете на код текущего метода, выполняемый после await .

Если потребитель моей библиотеки вызывает один из моих интерфейсных методов, должны ли они вызывать ConfigureAwait (false)

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

Если вы не знаете или не доверяете сторонней библиотеке, возможно, вам будет лучше сделать это, чтобы «быть в безопасности». Но если это библиотека, которую я использую, и я знаю, что все ее интерфейсные методы и внутренний код используют ConfigureAwait (false), мне не нужно при их вызове, верно?

Поскольку это не влияет на вызываемый вами метод, а скорее на выполнение кода после завершения возвращенной задачи, этот вопрос на самом деле не имеет смысла.

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

В этой ситуации мы не знаем, должен ли код, выполняемый после ожидания, выполняться в текущем контексте синхронизации или нет. Мы не знаем, что это за код, каким может быть текущий контекст синхронизации и зависит ли этот код от этого контекста. Если это код пользовательского интерфейса, а контекст — это контекст пользовательского интерфейса, то он должен выполняться в этом контексте. Если это контекст пользовательского интерфейса, и вы выполняете работу, отличную от пользовательского интерфейса, то вы хотите не запускаться в контексте.

Комментарии:

1. Поскольку у меня, очевидно, была перспектива ConfigureAwait повлиять на следующий уровень в стеке вызовов, и ваш ответ очень четко помог мне понять, что это влияет на текущий метод, можете ли вы по-прежнему обращаться, если это необходимо во всей библиотеке, или только в методе, который вызывает что-то внешнее. т. Е. Метод, который вызывает HttpWebRequest.GetResponseAsync() ? Или это негативно повлияет на потребителей, если не будет на каждом уровне в моей библиотеке ConfigureAwait ?

2. Или, если это правильно: Если в рамках того же метода, и особенно после await ввода async вызова, вы будете использовать SynchronizationContext . Тогда эти await вызовы ed, например, перед обратной записью в пользовательский интерфейс или доступом к HttpContext, должны настроить свою задачу с помощью ConfigureAwait(false) , чтобы такие вызовы не пытались вернуться к SynchronizationContext для будущего кода и, таким образом, приостановить вызов await ed async .

3. @Suamere Вы должны настроить await так, чтобы не использовать текущий контекст в любое время, когда код после await не нужно запускать в текущем контексте, как указано в ответе. Должен ли код, выполняемый после await , выполняться в текущем контексте, зависит от этого кода и от того, каков контекст. Большинство библиотек ничего не будут делать с текущим контекстом, но некоторые будут (например, библиотека, предназначенная для взаимодействия с определенным пользовательским интерфейсом).

4. Вы не настраиваете ожидание так, чтобы оно не использовало текущий контекст, чтобы избежать зависания. Если программа зависает, это потому, что кто-то заблокировал, когда не должен был. Вы настраиваете ожидание так, чтобы оно не использовало текущий контекст, чтобы избежать накладных расходов на синхронизацию, когда вы на самом деле не получаете от этого выгоды. Это незначительное улучшение производительности. (Но все же стоит делать, когда это уместно в библиотечном коде, вообще говоря).