Últimamente parece que no puedo tener suficiente del sorprendente patrón de espera asíncrona de C # 5.0. Donde has estado toda mi vida?
Estoy absolutamente encantado con la sintaxis simple, pero tengo una pequeña dificultad. Mi problema es que las funciones asíncronas tienen una declaración totalmente diferente de las funciones normales. Dado que solo las funciones asíncronas pueden esperar en otras funciones asíncronas, cuando intento portar algún código de bloqueo antiguo a asíncrono, tengo un efecto dominó de las funciones que tengo que convertir.
La gente se ha referido a esto como una infestación de zombis . Cuando async obtiene una mordida en su código, seguirá creciendo más y más. El proceso de portabilidad no es difícil, solo incluye async
la declaración y ajusta el valor de retorno Task<>
. Pero es molesto hacer esto una y otra vez al portar código síncrono antiguo.
Me parece que será mucho más natural si ambos tipos de funciones (asíncrona y sincronización antigua simple) tuvieran exactamente la misma sintaxis. Si este fuera el caso, la transferencia no requerirá esfuerzo y podría cambiar sin problemas entre las dos formas.
Creo que esto podría funcionar si seguimos estas reglas:
Las funciones asincrónicas ya no requerirán la
async
declaración. Sus tipos de devolución no tendrían que estar envueltosTask<>
. El compilador identificará una función asíncrona durante la compilación por sí mismo y realizará la tarea <> ajustando automáticamente según sea necesario.No más llamadas de disparar y olvidar a funciones asíncronas. Si desea llamar a una función asíncrona, deberá esperarla. De todos modos, casi nunca utilizo disparar y olvidar, y todos los ejemplos de condiciones de carrera locas o puntos muertos siempre parecen estar basados en ellos. Creo que son demasiado confusos y "fuera de contacto" con la mentalidad sincrónica que tratamos de aprovechar.
Si realmente no puede vivir sin disparar y olvidar, habrá una sintaxis especial para ello. En cualquier caso, no será parte de la sintaxis unificada simple de la que estoy hablando.
La única palabra clave que necesita para denotar una llamada asincrónica es
await
. Si ha esperado, la llamada es asíncrona. Si no lo hace, la llamada es sincrónica antigua (recuerde, ya no tenemos disparar y olvidar).El compilador identificará las funciones asíncronas automáticamente (ya que ya no tienen una declaración especial). La regla 4 hace que esto sea muy simple: si una función tiene una
await
llamada dentro, es asíncrona.
¿Esto podría funcionar? ¿O me estoy perdiendo algo? Esta sintaxis unificada es mucho más fluida y podría resolver la infestación de zombis por completo.
Algunos ejemplos:
// assume this is an async function (has await calls inside)
int CalcRemoteBalanceAsync() { ... }
// assume this is a regular sync function (has no await calls inside)
int CalcRemoteBalance() { ... }
// now let's try all combinations and see what they do:
// this is the common synchronous case - it will block
int b1 = CalcRemoteBalance();
// this is the common asynchronous case - it will not block
int b2 = await CalcRemoteBalanceAsync();
// choosing to call an asynchronous function in a synchronous manner - it will block
// this syntax was used previously for async fire-and-forget, but now it's just synchronous
int b3 = CalcRemoteBalanceAsync();
// strange combination - it will block since it's calling a synchronous function
// it should probably show a compilation warning though
int b4 = await CalcRemoteBalance();
Nota: esta es una continuación de una discusión relacionada interesante en SO
fuente
await
inmediato. Se puede hacer algo asívar task = FooAsync(); Bar(); await task;
. ¿Cómo haría esto en tu propuesta?async
. Creo que esa es una de las grandes ventajas deasync
-await
: que le permite componer fácilmente las operaciones asincrónicas (y no sólo en el más simple "iniciar una, espera por A, B empezar, espera para B" camino). Y ya existe una sintaxis especial exactamente para este propósito: se llamaawait
.Respuestas:
Su pregunta ya está respondida en la pregunta SO que ha vinculado.
Cuando salió por primera vez WinRT , los diseñadores describían cómo decidieron qué operaciones iban a ser asíncronas. Decidieron que cualquier cosa que tomara 50 ms o más sería asíncrona, y el resto de los métodos serían métodos ordinarios, no asíncronos.
¿Cuántos de los métodos tuvieron que ser reescritos para hacerlos asíncronos? Alrededor del 10 por ciento de ellos. El otro 90% no se vio afectado en absoluto.
Eric Lippert continúa explicando con detalles técnicos bastante sustanciales por qué optaron por no adoptar un enfoque único para todos. Básicamente dice eso
async
yawait
es una implementación parcial del estilo de paso continuo, y que optimizar ese estilo para que se adapte a todos los casos es un problema difícil.fuente
async
produciría infestaciones de zombis. Incluso si un método llama a otros 10 métodos, ¿no tiene que pasar alasync
nivel superior?