Actualización: ASP.NET Core no tiene unSynchronizationContext
. Si está en ASP.NET Core, no importa si lo 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 async
en ASP.NET.
Había leído que es más eficiente ya que no tiene que cambiar los contextos de hilo al contexto de hilo original.
Esto es cierto con las aplicaciones de interfaz de usuario, donde solo hay un hilo de interfaz de usuario al que debe "sincronizar" nuevamente.
En ASP.NET, la situación es un poco más compleja. Cuando un async
método reanuda la ejecución, toma un hilo del grupo de hilos ASP.NET. Si deshabilita el uso de la captura de contexto ConfigureAwait(false)
, entonces el hilo simplemente continúa ejecutando el método directamente. Si no deshabilita la captura de contexto, el hilo volverá a ingresar el contexto de solicitud y luego continuará ejecutando el método.
Por ConfigureAwait(false)
lo tanto, no le ahorra 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á intentando hacer una pequeña cantidad de procesamiento paralelo de una solicitud, pero realmente TPL es una mejor opción para la mayoría de esos escenarios.
Sin embargo, con ASP.NET Web Api, si su solicitud llega en un hilo, y espera alguna función y llama a ConfigureAwait (falso) que podría ponerlo en un hilo diferente cuando devuelva el resultado final de su función ApiController .
En realidad, solo hacer un await
puede hacer eso. Una vez que su async
método llega a un await
, el método se bloquea pero el subproceso vuelve al grupo de subprocesos. Cuando el método está listo para continuar, cualquier subproceso se extrae del grupo de subprocesos y se utiliza para reanudar el método.
La única diferencia que ConfigureAwait
hace en ASP.NET es si ese hilo ingresa al contexto de solicitud al reanudar el método.
Tengo más información de fondo en mi artículo de MSDNSynchronizationContext
y en mi async
entrada de blog de introducción .
HttpContext.Current
es operado por ASP.NETSynchronizationContext
, que fluye de manera predeterminada cuando ustedawait
, pero no lo haceContinueWith
. OTOH, el contexto de ejecución (incluidas las restricciones de seguridad) es el contexto mencionado en CLR a través de C #, y es fluido por ambosContinueWith
yawait
(incluso si lo usaConfigureAwait(false)
).ConfigureAwait
realidad solo tiene sentido cuando espera tareas , mientras queawait
actúa en cualquier "espera". Otras opciones consideradas fueron: ¿Debería el comportamiento predeterminado descartar el contexto si se encuentra en una biblioteca? ¿O tiene una configuración de compilador para el comportamiento de contexto predeterminado? Ambos fueron rechazados porque es más difícil simplemente leer el código y decir lo que hace.ConfigureAwait(false)
evitar un punto muerto basado enResult
/Wait
porque en ASP.NET no debería usarResult
/Wait
en primer lugar.Breve respuesta a su pregunta: No. No debe llamar
ConfigureAwait(false)
al nivel de aplicación de esa manera.TL; Versión DR de la respuesta larga: si está escribiendo una biblioteca donde 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 asincrónicos de manera bloqueante. Esto depende de la situación.Aquí hay una explicación un poco más detallada sobre la importancia del
ConfigureAwait
método (una cita de mi publicación de blog):Además, aquí hay dos excelentes 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 de la biblioteca asíncrona deberían considerar el uso de Task.ConfigureAwait (falso) .
Espero que esto ayude.
fuente
Task
camina por la pila para obtener elSynchronizationContext
, que está mal. SeSynchronizationContext
toma antes de la llamada alTask
y luego se continúa con el resto del códigoSynchronizationContext
siSynchronizationContext.Current
no es nulo.SynchronizationContext.Current
esté claro / o que la biblioteca se llame dentro de un enTask.Run()
lugar de tener que escribir en.ConfigureAwait(false)
toda la biblioteca de la clase?.ConfigureAwait(false)
s. Quizás sería más fácil para los autores de bibliotecas si ese fuera el comportamiento predeterminado, pero supongo que hacer un poco más difícil escribir una biblioteca correctamente es mejor que hacer un poco más difícil escribir una aplicación correctamente.El mayor inconveniente que he encontrado al usar ConfigureAwait (falso) es que la cultura del hilo se revierte a los valores predeterminados del sistema. Si ha configurado una cultura, por ejemplo ...
y está alojando en un servidor cuya cultura está configurada en en-EE. UU., Entonces encontrará antes de que ConfigureAwait (falso) se llame CultureInfo. es decir
Si su aplicación está haciendo algo que requiere un formato de datos específico de la cultura, deberá tener esto en cuenta al usar ConfigureAwait (falso).
fuente
ConfigureAwait(false)
se usa.Tengo algunas ideas generales sobre la implementación de
Task
:using
.ConfigureAwait
se introdujo en 4.5.Task
fue introducido en 4.0.Task.ContinueWith
b / c no se dio cuenta de que el cambio de contexto es costoso y está desactivado de forma predeterminada.Tengo algunas publicaciones sobre el tema, pero mi opinión, además de la buena respuesta de Tugberk, es que debes convertir todas las API en asíncronas e idealmente fluir el contexto. Como está haciendo asíncrono, simplemente puede usar continuaciones en lugar de esperar para que no se produzca un punto muerto, ya que no se realiza ninguna espera en la biblioteca y se mantiene el flujo para que se conserve el contexto (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.fuente
Task.Dispose
si quieres; simplemente no necesitas la gran mayoría de las veces. 2)Task
se introdujo en .NET 4.0 como parte de la TPL, que no necesitabaConfigureAwait
; cuandoasync
se agregó, reutilizaron elTask
tipo existente en lugar de inventar uno nuevoFuture
.Task
s; el "contexto" controlado porContinueWith
es unSynchronizationContext
oTaskScheduler
. Estos diferentes contextos se explican en detalle en el blog de Stephen Toub .Thread
s, pero ya no lo hace conContinueWith()
), esto hace que su respuesta sea confusa de leer.