false ejemplos configureawait await async c# asp.net-web-api task-parallel-library async-await

c# - ejemplos - Mejores prácticas para llamar a ConfigureAwait para todos los códigos del lado del servidor



configureawait c# (4)

Cuando tiene un código del lado del servidor (es decir, algo de ApiController ) y sus funciones son asíncronas, por lo que devuelven la Task<SomeObject> - ¿se considera la mejor práctica que cada vez que espere funciones que llame a ConfigureAwait(false) ?

Había leído que es más eficaz, ya que no tiene que cambiar los contextos de los hilos nuevamente al contexto de los hilos originales. Sin embargo, con ASP.NET Web Api, si su solicitud está entrando en un subproceso, usted espera alguna función y llama a ConfigureAwait(false) que podría ponerlo en un subproceso diferente cuando devuelva el resultado final de su función ApiController .

He escrito un ejemplo de lo que estoy hablando a continuación:

public class CustomerController : ApiController { public async Task<Customer> Get(int id) { // you are on a particular thread here var customer = await SomeAsyncFunctionThatGetsCustomer(id).ConfigureAwait(false); // now you are on a different thread! will that cause problems? return customer; } }


El mayor inconveniente que he encontrado con el uso de ConfigureAwait (falso) es que la cultura de subprocesos se ha revertido al valor predeterminado del sistema. Si has configurado una cultura por ejemplo ...

<system.web> <globalization culture="en-AU" uiCulture="en-AU" /> ...

y está alojando en un servidor cuya cultura está configurada en en-US, entonces encontrará que antes de que se llame a ConfigureAwait (false) CultureInfo. La cultura de la zona regresará en-AU y después de que obtenga en-US. es decir

// CultureInfo.CurrentCulture ~ {en-AU} await xxxx.ConfigureAwait(false); // CultureInfo.CurrentCulture ~ {en-US}

Si su aplicación está haciendo algo que requiere un formato de datos específico de la cultura, entonces deberá tener esto en cuenta al usar ConfigureAwait (falso).


Respuesta breve a su pregunta: No. No debe llamar a ConfigureAwait(false) en el nivel de la aplicación de esa manera.

TL; versión DR de la respuesta larga: si está escribiendo una biblioteca en la que no conoce a su consumidor y no necesita un contexto de sincronización (que no debería en una biblioteca, creo), siempre debe usar ConfigureAwait(false) . De lo contrario, los consumidores de su biblioteca pueden enfrentar puntos muertos al consumir sus métodos asíncronos de forma bloqueada. Esto depende de la situación.

Aquí hay una explicación un poco más detallada sobre la importancia del método ConfigureAwait (una cita de mi blog):

Cuando está esperando un método con la palabra clave await, el compilador genera un montón de código en su nombre. Uno de los propósitos de esta acción es manejar la sincronización con el subproceso de la interfaz de usuario (o principal). El componente clave de esta característica es SynchronizationContext.Current que obtiene el contexto de sincronización para el hilo actual. SynchronizationContext.Current se completa según el entorno en el que se encuentre. El método GetAwaiter de la tarea busca SynchronizationContext.Current . Si el contexto de sincronización actual no es nulo, la continuación que se pasa a ese observador se volverá a publicar en ese contexto de sincronización.

Al consumir un método, que utiliza las nuevas funciones de lenguaje asíncrono, de forma bloqueada, terminará con un interbloqueo si tiene un SynchronizationContext disponible. Cuando esté consumiendo dichos métodos de forma bloqueada (esperando el método Tarea con Espera o tomando el resultado directamente de la propiedad Resultado de la Tarea), bloqueará el hilo principal al mismo tiempo. Cuando finalmente la Tarea se complete dentro de ese método en el conjunto de subprocesos, invocará la continuación para volver a publicar en el subproceso principal porque SynchronizationContext.Current está disponible y se captura. Pero hay un problema aquí: el hilo de la interfaz de usuario está bloqueado y tienes un punto muerto.

Además, aquí hay dos grandes artículos para usted que son exactamente para su pregunta:

Finalmente, hay un gran video corto de Lucian Wischik exactamente sobre este tema: los métodos asíncronos de la biblioteca deberían considerar el uso de Task.ConfigureAwait (falso) .

Espero que esto ayude.


Tengo algunos pensamientos generales sobre la implementación de la Task :

  1. La tarea es desechable, pero no debemos usarla.
  2. ConfigureAwait fue introducido en 4.5. Task se introdujo en 4.0.
  3. Los subprocesos .NET siempre se usaban para fluir el contexto (ver C # a través del libro de CLR) pero en la implementación predeterminada de Task.ContinueWith no b / c se realizó que el cambio de contexto es costoso y está desactivado de manera predeterminada.
  4. El problema es que a un desarrollador de bibliotecas no le debe importar si sus clientes necesitan flujo de contexto o no, por lo tanto, no debe decidir si fluye el contexto o no.
  5. [Agregado más adelante] El hecho de que no haya una respuesta autorizada y una referencia adecuada y seguimos peleando por esto significa que alguien no ha hecho bien su trabajo.

Tengo algunas posts sobre el tema, pero mi opinión, además de la buena respuesta de Tugberk, es que debería hacer que todas las API sean asíncronas e idealmente fluir el contexto. Ya que está haciendo async, puede simplemente usar continuaciones en lugar de esperar, por lo que no se producirá ningún interbloqueo, ya que no se realiza ninguna espera en la biblioteca y mantiene el flujo para que el contexto se mantenga (como HttpContext).

El problema es cuando una biblioteca expone una API síncrona pero usa otra API asíncrona; por lo tanto, debe usar Wait() / Result en su código.


Actualización: ASP.NET Core no tiene un SynchronizationContext . Si está en ASP.NET Core, no importa si usa ConfigureAwait(false) o no.

Para ASP.NET "Completo" o "Clásico" o lo que sea, el resto de esta respuesta aún se aplica.

Publicación original (para ASP.NET no Core):

Este video del equipo de ASP.NET tiene la mejor información sobre el uso de async en ASP.NET.

Había leído que es más eficaz, ya que no tiene que cambiar los contextos de los hilos nuevamente al contexto de los hilos originales.

Esto es cierto con las aplicaciones de UI, donde solo hay un subproceso de UI que tiene que "sincronizar" de nuevo.

En ASP.NET, la situación es un poco más compleja. Cuando un método async reanuda la ejecución, toma un subproceso del grupo de subprocesos de ASP.NET. Si deshabilita la captura de contexto usando ConfigureAwait(false) , entonces el hilo simplemente continúa ejecutando el método directamente. Si no deshabilita la captura de contexto, el hilo reingresará el contexto de solicitud y luego continuará ejecutando el método.

Entonces, ConfigureAwait(false) no te guarda un salto de hilo en ASP.NET; le ahorra el reingreso del contexto de solicitud, pero esto normalmente es muy rápido. ConfigureAwait(false) podría ser útil si está tratando de hacer una pequeña cantidad de procesamiento paralelo de una solicitud, pero realmente TPL es un mejor ajuste para la mayoría de esos escenarios.

Sin embargo, con ASP.NET Web Api, si su solicitud está entrando en un subproceso, usted espera alguna función y llama a ConfigureAwait (falso) que podría ponerlo en un subproceso diferente cuando devuelva el resultado final de su función ApiController .

En realidad, solo hacer una await puede hacer eso. Una vez que su método async alcanza una await , el método se bloquea pero el hilo vuelve al grupo de hilos. Cuando el método está listo para continuar, cualquier hilo se arrebata del grupo de hilos y se usa para reanudar el método.

La única diferencia que hace ConfigureAwait en ASP.NET es si ese hilo entra en el contexto de la solicitud al reanudar el método.

Tengo más información de fondo en mi artículo de MSDN sobre SynchronizationContext y mi entrada de blog de introducción async .