De acuerdo, suena extraño, pero el código es muy simple y explica bien la situación.
public virtual async Task RemoveFromRoleAsync(AzureTableUser user, string role)
{
AssertNotDisposed();
var roles = await GetRolesForUser(user);
roles.Roles = RemoveRoles(roles.Roles, role);
await Run(TableOperation.Replace(roles));
}
(Sé que estoy hablando más o menos en el resumen a continuación, pero lo anterior es un método real de lo que será el código de producción real que realmente está haciendo lo que estoy preguntando aquí, y estoy realmente interesado en que realmente revise para la corrección con respecto al patrón asíncrono / espera).
Me encuentro con este patrón cada vez más a menudo ahora que estoy usando async
/ await
more. El patrón consta de la siguiente cadena de eventos:
- Espero una llamada inicial que me proporciona información sobre la que necesito trabajar
- Trabaja en esa información sincrónicamente
- Espere una llamada final que guarde el trabajo actualizado
El bloque de código anterior es típicamente cómo hago para manejar estos métodos. Yo await
la primera llamada, que tengo que hacerlo porque es asíncrona. A continuación, hago el trabajo que necesito hacer, que no está vinculado a recursos ni a IO, por lo que no es asíncrono. Finalmente, guardo mi trabajo, que también es una async
llamada, y fuera del culto a la carga await
.
¿Pero es esta la forma más eficiente / correcta de manejar este patrón? Me parece que podría saltarme await
la última llamada, pero ¿y si falla? ¿Y debo usar un Task
método como ContinueWith
encadenar mi trabajo sincrónico con la llamada original? Ahora mismo estoy en un punto en el que no estoy seguro de si estoy manejando esto correctamente.
Dado el código en el ejemplo , ¿hay una mejor manera de manejar esta cadena de llamadas del método async / sync / async?
fuente
Respuestas:
Sí, creo que esta es la forma correcta de hacerlo.
No puedes saltarte el segundo
await
. Si lo hiciera, el método parecería completarse demasiado pronto (antes de que se realizara realmente la eliminación), y nunca descubriría si la eliminación falló.No veo cómo ayudaría
ContinueWith()
nada de eso aquí. Puede usarlo para evitar el usoawait
, pero haría que su código sea más complicado y menos legible. Y ese es el objetivo deawait
: simplificar la escritura de código asincrónico, en comparación con el uso de continuaciones.fuente
La forma de manejar este patrón es garantizar que todas las E / S sean asíncronas. Los métodos de E / S síncronas hacen que el subproceso actual se bloquee mientras espera una respuesta del destino de E / S (red, sistema de archivos, etc.).
Otra cosa a tener en cuenta es que
await
debe usarse cuando necesite un valor de retorno o cuando necesite que el código esperado termine antes de hacer otra cosa. Si no necesita ninguna de esas cosas, puede "disparar y olvidar" su método asincrónicoTask.Run
.Por lo tanto, para el uso más eficiente de los recursos informáticos, si hay
RemoveRoles
alguna E / S, debería convertirseawait RemoveRolesAsync
y los métodos de E / S invocadosRemoveRolesAsync
también deberían ser asíncronos (y posiblemente esperados).Si el rendimiento no es su mayor preocupación, entonces está bien hacer algunas E / S sincrónicas en un hilo asíncrono. Sin embargo, es una deuda técnica. (En este caso, es posible que desee llamar al primer método asíncrono
ConfigureAwait
, según dónde se esté ejecutando el código).Aquí hay una mirada más profunda a las mejores prácticas: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
Aquí hay algunas notas sobre el comportamiento de ConfigureAwait en diferentes entornos como ASP.NET, WebAPI, etc. - /programming/13489065/best-practice-to-call-configureawait-for-all-server-side -código
fuente