En este artículo de MSDN , se proporciona el siguiente código de ejemplo (ligeramente editado por brevedad):
public async Task<ActionResult> Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Department department = await db.Departments.FindAsync(id);
if (department == null)
{
return HttpNotFound();
}
return View(department);
}
El FindAsync
método recupera un Department
objeto por su ID y devuelve a Task<Department>
. Luego, el departamento se verifica de inmediato para ver si es nulo. Según tengo entendido, pedir el valor de la Tarea de esta manera bloqueará la ejecución del código hasta que se devuelva el valor del método esperado, haciendo de esta una llamada síncrona.
¿Por qué harías esto? ¿No sería más simple simplemente llamar al método síncrono Find(id)
, si vas a bloquear inmediatamente de todos modos?
c#
.net
asp.net-mvc
async
Robert Harvey
fuente
fuente
... else return null;
Luego, deberá verificar que el método realmente encontró el departamento que solicitó.Respuestas:
No exactamente.
Cuando llama,
await db.Departments.FindAsync(id)
la tarea se envía y el subproceso actual se devuelve al grupo para que lo utilicen otras operaciones. El flujo de ejecución está bloqueado (como sería independientemente de usarlodepartment
inmediatamente después, si entiendo las cosas correctamente), pero el hilo en sí mismo puede ser utilizado libremente por otras cosas mientras espera que la operación se complete fuera de la máquina (y señalado por un evento o puerto de finalización).Si llamó,
d.Departments.Find(id)
entonces el hilo se queda allí y espera la respuesta, a pesar de que la mayor parte del procesamiento se realiza en la base de datos.Está efectivamente liberando recursos de la CPU cuando el disco está vinculado.
fuente
await
hice fue firmar en el resto del método como una continuación en el mismo hilo (hay excepciones; algunos métodos asíncronos hacen girar su propio hilo), o iniciar sesión en elasync
método como una continuación en el mismo hilo y permitir el código restante para ejecutar (como puede ver, no tengo muy claro cómoasync
funciona). Lo que estás describiendo suena más como una forma sofisticada deThread.Sleep(untilReturnValueAvailable)
ConfigureAwait
iirc).await
llamadopublic async Task<ActionResult> Details(int? id)
. De lo contrario, la llamada original simplemente se bloqueará, esperandodepartment == null
a resolverse.await ...
"regresa" laFindAsync
llamada ha finalizado. Eso es lo que espera. Se llama esperar porque hace que su código espere cosas. (Pero tenga en cuenta que no es lo mismo que hacer que el hilo actual espere cosas)Realmente odio que ninguno de los ejemplos muestre cómo es posible esperar algunas líneas antes de esperar la tarea. Considera esto.
Este es el tipo de código que los ejemplos fomentan, y tienes razón. Hay poco sentido en esto. Libera el hilo principal para hacer otras cosas, como responder a la entrada de la interfaz de usuario, pero el verdadero poder de async / wait es que puedo seguir haciendo otras cosas fácilmente mientras espero que se complete una tarea potencialmente larga. El código anterior se "bloqueará" y esperará para ejecutar la línea de impresión hasta que obtengamos Foo & Bar. Sin embargo, no hay necesidad de esperar. Podemos procesar eso mientras esperamos.
Ahora, con el código reescrito, no nos detenemos y esperamos nuestros valores hasta que tengamos que hacerlo. Siempre estoy atento a este tipo de oportunidades. Ser inteligente sobre cuándo esperamos puede conducir a mejoras significativas en el rendimiento. Tenemos varios núcleos en estos días, también podría usarlos.
fuente
await
y dejes que el hilo haga cosas completamente diferentes.Entonces hay más que sucede detrás de escena aquí. Async / Await es azúcar sintáctico. Primero mire la firma de la función FindAsync. Devuelve una tarea. Ya estás viendo la magia de la palabra clave, desempaqueta esa Tarea en un Departamento.
La función de llamada no se bloquea. Lo que sucede es que la asignación al departamento y todo lo que sigue a la palabra clave en espera se encuadra en un cierre y, para todos los intentos y propósitos, se pasa al método Task.ContinueWith (la función FindAsync se ejecuta automáticamente en un hilo diferente).
Por supuesto, hay más cosas que suceden detrás de escena porque la operación se vuelve a ordenar al hilo original (por lo que ya no tiene que preocuparse por sincronizar con la interfaz de usuario al realizar una operación en segundo plano) y en el caso de que la función de llamada sea Async ( y ser llamado asincrónicamente) lo mismo sucede en la pila.
Entonces, lo que sucede es que obtienes la magia de las operaciones Async, sin las trampas.
fuente
No, no regresará de inmediato. La espera hace que el método llame asíncrono. Cuando se llama a FindAsync, el método Detalles regresa con una tarea que no ha finalizado. Cuando FindAsync finalice, devolverá su resultado a la variable de departamento y reanudará el resto del método Detalles.
fuente
async
await
generalmente no crea nuevos subprocesos, e incluso si lo hiciera, aún debe esperar a que el valor del departamento descubra si es nulo.public async Task<ActionResult>
también debe serawait
editada.await
no debe mezclarse con.Wait()
o.Result
, ya que esto puede causar puntos muertos. La cadena asíncrona / espera generalmente termina en una función con unaasync void
firma, que se usa principalmente para controladores de eventos o para funciones invocadas directamente por elementos de la interfaz de usuario.Me gusta pensar que "async" es un contrato, un contrato que dice "puedo ejecutar esto de forma asincrónica si lo necesitas, pero también puedes llamarme como cualquier otra función síncrona".
Es decir, un desarrollador estaba haciendo funciones y algunas decisiones de diseño los llevaron a hacer / marcar un montón de funciones como "asíncronas". La persona que llama / consumidor de las funciones tiene la libertad de usarlas según lo decida. Como dice, puede llamar en espera justo antes de la llamada a la función y esperarla, de esta manera la ha tratado como una función síncrona, pero si lo desea, puede llamarla sin esperar como
y después, digamos, 10 líneas abajo de la función que llamas
por lo tanto, lo trata como una función asincrónica.
Tu decides.
fuente
"si vas a bloquear inmediatamente de todos modos", la respuesta es "Sí". Solo cuando necesite una respuesta rápida, esperar / async tiene sentido. Por ejemplo, el subproceso de la interfaz de usuario llega a un método realmente asíncrono, el subproceso de la interfaz de usuario regresará y continuará escuchando el clic del botón, mientras que el siguiente código "espera" será ejecutado por otro subproceso y finalmente obtendrá el resultado.
fuente