¿Por qué necesitamos la palabra clave async?

14

Acabo de empezar a jugar con async / await en .Net 4.5. Algo por lo que tengo curiosidad al principio, ¿por qué es necesaria la palabra clave asíncrona? La explicación que leí fue que es un marcador, por lo que el compilador sabe que un método espera algo. Pero parece que el compilador debería poder resolver esto sin una palabra clave. Entonces, ¿qué más hace?

    
pregunta ConditionRacer 18.02.2013 - 22:25

4 respuestas

19

Aquí hay varias respuestas, y todas hablan sobre lo que hacen los métodos asíncronos, pero ninguna responde a la pregunta, por lo que async es necesario como palabra clave que va en la declaración de la función.

No es "dirigir al compilador a transformar la función de una manera especial"; await solo podría hacer eso. ¿Por qué? Debido a que C # ya tiene otro mecanismo donde la presencia de una palabra clave especial en el cuerpo del método hace que el compilador realice transformaciones extremas (y muy similares a async/await ) en el cuerpo del método: yield .

Excepto que yield no es su propia palabra clave en C #, y entender por qué también se explicará async . A diferencia de la mayoría de los idiomas que admiten este mecanismo, en C # no puede decir yield value; . En su lugar, debe decir yield return value; . ¿Por qué? Debido a que se agregó al lenguaje después de que C # ya existía, y era bastante razonable suponer que alguien, en algún lugar, podría haber usado yield como el nombre de una variable. Pero como no había un escenario preexistente en el que <variable name> return fuera sintácticamente correcto, yield return se agregó al lenguaje para permitir la introducción de generadores mientras se mantiene el 100% de compatibilidad con el código existente.

Y esta es la razón por la que se agregó async como un modificador de función: para evitar romper el código existente que usaba await como nombre de variable. Como ya no existían los métodos async , no se invalida ningún código antiguo y, en el nuevo código, el compilador puede usar la presencia de la etiqueta async para saber que await debe tratarse como una palabra clave y no como un identificador.

    
respondido por el Mason Wheeler 05.11.2015 - 17:14
6

cambia el método de un método normal a un objeto con devolución de llamada que requiere un enfoque totalmente diferente para la generación de código

y cuando sucede algo drástico como eso, es costumbre que lo denuncie claramente (aprendimos esa lección de C ++)

    
respondido por el ratchet freak 18.02.2013 - 22:33
4

La idea general con palabras clave como "asíncrono" o "inseguro" es eliminar la ambigüedad en cuanto a cómo debe tratarse el código que modifican. En el caso de la palabra clave async, le dice al compilador que trate el método modificado como algo que no es necesario que regrese de inmediato. Esto permite que el hilo en el que se utiliza este método continúe sin tener que esperar los resultados de ese método. Es efectivamente una optimización de código.

    
respondido por el World Engineer 18.02.2013 - 22:34
0

Está bien, aquí está mi opinión.

Hay algo llamado coroutines que se conoce desde hace décadas. ("Knuth and Hopper" -class "durante décadas") Son generalizaciones de subrutinas , en las que no solo obtienen y liberan el control en la función de inicio y retorno de la función, sino que también lo hacen en puntos específicos ( puntos de suspensión ). Una subrutina es una coroutina sin puntos de suspensión.

Son sencillos de implementar con macros C, como se muestra en el siguiente documento sobre "protothreads". ( enlace ) Léalo. Voy a esperar ...

La conclusión de esto es que las macros crean un gran switch y una etiqueta case en cada punto de suspensión. En cada punto de suspensión, la función almacena el valor de la etiqueta case inmediatamente siguiente, para que sepa dónde reanudar la ejecución la próxima vez que se llame. Y devuelve el control a la persona que llama.

Esto se hace sin modificar el flujo aparente de control del código descrito en el "protothread".

Imagina ahora que tienes un gran bucle que llama a todos estos "protothreads" a su vez, y obtienes simultáneamente la ejecución de "protothreads" en un solo hilo.

Este enfoque tiene dos inconvenientes:

  1. No puede mantener el estado en las variables locales entre las reanudaciones.
  2. No puede suspender el "protothread" de una profundidad de llamada arbitraria. (todos los puntos de suspensión deben estar en el nivel 0)

Hay soluciones para ambos:

  1. Todas las variables locales deben ajustarse al contexto del protothread (contexto que ya es necesario por el hecho de que el protothread debe almacenar su próximo punto de reanudación)
  2. Si siente que realmente necesita llamar a otro protothread desde un protothread, "genere" un protothread infantil y suspenda hasta que el niño termine.

Y si tuvieras soporte de compilador para hacer el trabajo de reescritura que hacen las macros y la solución, bueno, simplemente puedes escribir el código de tu protothread tal como pretendes e insertar puntos de suspensión con una palabra clave.

Y esto es de lo que se trata async y await : crear coroutines (sin pila).

Las coroutinas en C # se reifican como objetos de la clase (genérica o no genérica) Task .

Encuentro estas palabras clave muy confusas. Mi lectura mental es:

  • async como "suspensible"
  • await como "suspender hasta que se complete"
  • Task como "futuro ..."

Ahora. ¿Realmente necesitamos marcar la función async ? Además de decir que debería desencadenar los mecanismos de reescritura de código para hacer que la función sea una rutina, resuelve algunas ambigüedades. Considera este código.

public Task<object> AmIACoroutine() {
    var tcs = new TaskCompletionSource<object>();
    return tcs.Task;
}

Suponiendo que async no es obligatorio, ¿es esto una rutina o una función normal? ¿Debería el compilador reescribirlo como un coroutine o no? Ambos podrían ser posibles con diferentes semánticas eventuales.

    
respondido por el Laurent LA RIZZA 05.11.2015 - 11:15

Lea otras preguntas en las etiquetas