2008-11-19 35 views

Respuesta

18

Las corutinas verdaderas requieren soporte de sus herramientas; deben ser implementadas por el compilador y respaldadas por el marco subyacente.

Un ejemplo del mundo real de Coroutines se encuentra con la palabra clave "yield return" proporcionada en C# 2.0, que le permite escribir un método que devuelve múltiples valores para el bucle.

El "rendimiento de retorno" tiene limitaciones, sin embargo, la implementación utiliza una clase auxiliar para capturar el estado, y solo admite un caso específico de coroutine como generador (iterador).

En el caso más general, la ventaja de corrutinas es que hacen ciertos cálculos basados ​​estado mucho más fáciles de expresar y más fácil de entender - implementar una máquina de estado como un conjunto de co-rutinas pueden ser más elegante que los enfoques más comunes . Pero hacer esto requiere soporte y herramientas que aún no existen en C# o Java.

+0

+1 Se trata de "estado" imo. –

11

Coroutines son útiles para implementar patrones de productor/consumidor.

Por ejemplo, Python introdujo coroutines en una característica de lenguaje llamada generators, que estaba destinada a simplificar la implementación de iteradores.

También pueden ser útiles para implementar la multitarea cooperativa, donde cada tarea es una corutina que cede a un programador/reactor.

+0

No puedo comentar sobre los generadores de Python, pero he usado una construcción de generador antes, y encontré el concepto ingenioso con grandes problemas de juguete, pero muy difícil de usar en la codificación real. –

+2

Los generadores son muy útiles y ampliamente utilizados en Python de hoy. Pueden producir un código mucho más simple y legible que el equivalente escrito con un objeto, poniendo información de estado en los miembros. Pero no son co-rutinas completas y tienen sus limitaciones en comparación. – bobince

40

Algunas buenas respuestas que describen qué corutinas son.

Pero para un caso de uso real. Toma un servidor web. Tiene múltiples conexiones simultáneas, y quiere programar la lectura y escritura de todas ellas.

Esto se puede implementar utilizando coroutines. Cada conexión es una corutina que lee/escribe una pequeña cantidad de datos, luego "cede" el control al programador, que pasa a la siguiente corutina (que hace lo mismo) mientras recorremos todas las conexiones disponibles.

+5

No sé por qué esto languideció sin un +1 por tanto tiempo. Un servidor web impulsado por Coroutine, presumiendo que las rutinas se diseñaron correctamente para el cálculo por partes, sería muy útil para un servidor web con mucho peso cuando se trata de rendimiento y sería mucho más fácil de entender que una máquina administrada por una máquina de estado. –

+0

teniendo en cuenta que el sistema operativo puede decirle qué conexiones necesitan su atención, esto parece un enfoque ineficiente. – xaxxon

18

Muchos de ellos, por ejemplo:

grep TODO *.c *.h | wc -l 

La tubería de arriba es exactamente un corrutina: grep el comando genera una secuencia de líneas que van a un buffer, el comando wc "se los come"; si el buffer se llena, el grep "bloquea" hasta que el buffer se vacía, y si el buffer está vacío, el comando wc espera nueva entrada.

Lo que pasa con las coroutines es que a menudo se usan ahora en patrones más restringidos, como los generadores Python mencionados, o como tuberías.

Si desea ver más en ellos, consulte los artículos de Wikipedia, especialmente en coroutines y iterators.

+1

¡Nunca había pensado en eso! ¡Brillante! – geckos

6

Como un ejemplo más específico en la línea de productor/consumidor, algo tan simple como el humilde programa de informes por lotes podría usar co-rutinas.

El consejo clave para ese ejemplo es tener un trabajo no trivial para consumir datos de entrada (por ejemplo, analizar datos o acumular cargos y pagos en una cuenta), y un trabajo no trivial para producir el resultado. Cuando tenga estas características:

  • Es fácil organizar el código del lado de entrada si puede "emitir" unidades de trabajo en varios lugares.
  • También es fácil organizar/entender el código del lado de salida si puede "agarrar" la siguiente unidad de trabajo en una estructura de control anidada.

luego las coronas y las colas son buenas técnicas para tener a su disposición.

9

Coroutines puede ser útil cada vez que un sistema tiene dos o más piezas de código cuya representación más natural sería como una serie secuencial de pasos que implican una gran cantidad de espera.

Por ejemplo, considere un dispositivo que tiene una interfaz de usuario de teclado y LCD y un módem, y necesita usar el módem para llamar e informar periódicamente su estado independientemente de lo que esté haciendo el usuario en el teclado. La mejor manera de escribir la interfaz de usuario puede ser utilizar funciones como "input_numeric_value (& CONV_SPEED_FORMAT, & conveyor_speed);" que volverá cuando un usuario haya ingresado un valor, y la mejor forma de manejar la comunicación puede ser funciones de uso como "wait_for_carrier();" que volverá cuando la unidad se haya conectado o haya determinado que no va a funcionar.

Sin corutinas, el subsistema UI o el subsistema de módem debería implementarse utilizando una máquina de estado. El uso de corrutinas hace posible que ambos subsistemas se escriban con el estilo más natural. Tenga en cuenta que es importante que ninguno de los subsistemas pase mucho tiempo sin poner las cosas en un estado "consistente" y llame a yield(), ni llama a yield() sin poner primero las cosas en un estado "consistente", pero generalmente no es difícil cumplirlas restricciones

Tenga en cuenta que si bien uno podría utilizar la multitarea completa, eso requiere el uso de bloqueos en cualquier lugar en cualquier momento en que se modifique el estado compartido. Como el conmutador de coroutine nunca cambiará cosas excepto en las llamadas yield(), cualquiera de las dos puede alterar libremente el estado compartido siempre que asegure que todo esté en orden antes de la próxima producción y esté preparado para que la otra rutina altere el estado ". durante "el rendimiento().

14

Sé que esto es casi 5 años desde que se formuló la pregunta, pero me sorprende que nadie mencionó el caso de uso de juegos donde las coroutinas se usan mucho para medir el tiempo de un cálculo.

Para mantener una velocidad de fotogramas constante en un juego, digamos 60 fps, tiene alrededor de 16.6ms para ejecutar el código en cada fotograma. Eso incluye simulación de física, procesamiento de entrada, dibujo/pintura.

Digamos que su método se ejecuta en cada cuadro. Si su método tarda mucho tiempo y termina abarcando varios fotogramas, va a escalonar el resto del cálculo en el bucle del juego, lo que hace que el usuario vea "jank".

Lo que las coroutines le permiten hacer es, de alguna manera, cortar el tiempo de este cálculo para que se ejecute un poco en cada fotograma. Para que eso suceda, corutinas esencialmente permite que el método "ceda" el cálculo a la "persona que llama" (en este caso, el bucle del juego) para que la próxima vez que se llame se reanude desde donde se quedó. .

+0

Gracias, he pasado una hora tratando de obtener esto, pero su ejemplo es el que realmente hizo que haga clic para mí. –

Cuestiones relacionadas