Bibliotecas Coroutine disponibles en Java
(9)
¿Qué piensas de esta biblioteca de continuaciones escrita por Matthias Mann? He copiado los pros y los contras del sitio web del creador para facilitar el debate. Es importante mirar las pruebas en el código fuente para ver más allá del ejemplo en el sitio web.
http://www.matthiasmann.de/content/view/24/26/
Vamos a empezar con lo que obtienes:
- Escriba un código secuencial simple: ya no necesita crear máquinas de estado a mano
- No se crean ni necesitan subprocesos, no hay problemas de sincronización de subprocesos múltiples
- No hay creación de basura de la ejecución del código
- Muy pequeña sobrecarga de tiempo de ejecución
- Solo se modifican las llamadas a métodos suspendibles: todas las llamadas a su biblioteca estándar (como java.util. * Etc) no se ven afectadas en absoluto.
- Soporte completo de serialización
- Puede almacenar el estado de ejecución de las rutinas como parte de su estado de juego en su partida guardada sin ningún código adicional. Por supuesto, esto requiere que las clases y los tipos de datos que utiliza en sus revistas sean serializables. Soporte completo para manejo de excepciones y finalmente bloques.
- El preprocesamiento sin conexión no ralentiza el tiempo de carga de la aplicación Por supuesto, también es posible la instrumentación en tiempo de ejecución.
- Muy pequeña biblioteca de tiempo de ejecución: menos de 10 KByte (JAR sin comprimir) Licencia BSD
Con todas estas excelentes características, puede que esté preguntando por los inconvenientes. Bueno, por supuesto, hay algunos inconvenientes:
- Los constructores y los inicializadores estáticos no pueden ser suspendidos
- Los métodos suspendibles no se pueden sincronizar o sincronizar bloques.
- Debe descargar la biblioteca ASM3 para ejecutar la tarea de instrumentación
- No se puede llamar método suspendible con reflexión.
El problema de la sincronización se puede solucionar poniendo un código que requiera el uso de la sincronización en su propio método.
Me gustaría hacer algunas cosas en Java que serían más claras si se escribieran usando rutinas concurrentes, pero para las cuales los subprocesos completos son un exceso excesivo. La respuesta, por supuesto, es el uso de coroutines , pero no parece haber ningún soporte de coroutine en las bibliotecas estándar de Java y un Google rápido trae sugerencias sugerentes aquí o allá, pero nada sustancial.
Esto es lo que he encontrado hasta ahora:
- JSIM tiene una clase de coroutine, pero parece bastante pesado y se confunde, aparentemente, con hilos en puntos. El objetivo de esto es reducir la complejidad de los subprocesos completos, no agregarlos. Además, no estoy seguro de que la clase pueda extraerse de la biblioteca y usarse de forma independiente.
- Xalan tiene una clase de conjuntos de coroutine que hace cosas parecidas a las de un coroutine, pero de nuevo es dudoso que esto pueda extraerse de manera significativa de la biblioteca general. También parece que se ha implementado como una forma de grupo de subprocesos estrechamente controlada, no como corrutinas reales.
- Hay un proyecto de Google Code que se parece a lo que busco, pero en cualquier caso parece más pesado que el uso de hilos. Básicamente estoy nervioso por algo que requiere que el software cambie dinámicamente el bytecode de JVM en tiempo de ejecución para hacer su trabajo. Esto parece una exageración y algo que causará más problemas que los que podrían resolver las coroutinas. Además, parece que no implementa todo el concepto de coroutine. A mi vista, le da una característica de
yield
que solo regresa al invocador. Las coroutinas adecuadas permiten que elyield
transfiera el control a cualquier coroutina conocida directamente. Básicamente, esta biblioteca, de peso pesado y temible como es, solo le brinda soporte para iteradores, no para las coroutinas en general. - El prometedor nombre de Coroutine para Java falla porque es una solución específica de la plataforma (obviamente utilizando JNI).
Y eso es todo lo que he encontrado.
Sé sobre el soporte nativo de JVM para coroutines en la máquina Da Vinci y también conozco el truco de continuación de JNI para hacer esto. Sin embargo, estas no son realmente buenas soluciones para mí, ya que no necesariamente tendría control sobre en qué VM o plataforma se ejecutaría mi código. (De hecho, cualquier sistema de manipulación de bytecode sufriría problemas similares; sería mejor si fuera Java puro, si fuera posible. La manipulación de bytecode en tiempo de ejecución me impediría usar esto en Android, por ejemplo).
Entonces, ¿alguien tiene algún puntero? ¿Es esto posible? Si no, ¿será posible en Java 7?
Editado para añadir:
Solo para asegurarnos de que la confusión está contenida, esta es una pregunta relacionada con la otra , pero no es la misma. Este está buscando una implementación existente en un intento por evitar reinventar la rueda innecesariamente. La otra es una pregunta relacionada con la forma en que se implementaría Coroutines en Java en caso de que esta pregunta no tenga respuesta. La intención es mantener diferentes preguntas en diferentes hilos.
Más editado para añadir:
La respuesta está seleccionada . Algunos comentarios, sin embargo, están en orden. La biblioteca señalada no es una biblioteca de coroutine, por lo que técnicamente no responde a mi pregunta. Dicho esto, sin embargo, tiene dos ventajas sobre el proyecto de Google Code vinculado anteriormente:
- Ambas soluciones utilizan la manipulación de bytecode, pero la biblioteca seleccionada permite la manipulación estática de bytecode que la hace utilizable en Android y otras pilas JVM no compatibles.
- El proyecto de Google Code no hace coroutines completos. Si bien la biblioteca de la respuesta ni siquiera hace coroutines, hace algo más importante: proporciona una buena herramienta fundamental para rodar mis propias coroutines con todas las funciones.
El marco Kilim implementa las corrutinas mediante la reescritura de códigos de bytes. Lo he usado para implementar procesos Erjang en Erjang , y es muy estable y sorprendentemente rápido por la cantidad de reescritura de bytecode que se lleva a cabo.
Las rutinas de Kilim interactúan mediante el uso de buzones de correo, por lo que utilizo el marco para modelar actores de Erlang. Pero también se puede utilizar para hacer corrutinas en un modelo de memoria compartida.
El marco de juego ahora proporciona continuaciones con Javaflow. Debido a que Play brinda tanta conveniencia en otras áreas, es posible que desee comenzar con él.
http://www.playframework.org/documentation/1.2RC2/releasenotes-1.2#Continuations
Hay una nueva biblioteca de framework coroutine disponible para Java. Se implementa en Java puro, por lo que no es necesario que los Agentes JNI o Java se ejecuten por separado. Es de código abierto y se puede descargar desde GitHub:
https://github.com/esoco/coroutines
Una introducción al marco se puede encontrar en Medio:
Revisé la pregunta relacionada que vinculaste y por mi vida realmente no puedo entender qué hay de malo en los hilos. En Java, los subprocesos nunca son subprocesos nativos, son meras unidades de ejecución aisladas que, según el contexto, pueden funcionar como su propio subproceso nativo si es beneficioso, es decir, dividir grandes bloques de ejecución en propios subprocesos es inteligente, a la vez que se mantienen los pequeños en unos pocos Es más sensible debido a la sobrecarga.
Dicho esto, Java / JDK todavía no tiene coroutines disponibles para programadores de alto nivel, todavía. JDK7 ( cuando sea que salga ) tendrá lo que se conoce como jsr166y que es un framework Fork / Join por Doug Lea. Para información técnica, consulte este PDF por mr. Lea él mismo. En la práctica, Fork / Join agrega otro nivel de granularidad ( ¡útil! ) Sobre el modelo de subprocesamiento interno de Java que debería ayudarlo a lograr lo que desea.
Si está utilizando Java, hay 2 opciones disponibles a finales de 2017:
Ambos se basan en commons-javaflow: reescriben tu código a nivel de bytecode para que las cosas funcionen.
Coroutines : el lado Coroutines es que es rápido, es compatible con todos los principales sistemas de compilación y es compatible con la serialización / control de versiones de sus coroutines. El inconveniente es que la API tiene algunas desviaciones de commons-javaflow.
Vsilaev mantiene Tasclate-Javaflow : no lo he usado, por lo que no puedo hablar sobre él, pero se mantiene y, al observar los ejemplos, su API se alinea más cerca de la de commons-javaflow.
También hay funciones de idioma en Kotlin y Scala (y quizás otros idiomas basados en JVM) que le permiten usar coroutines. Pero, antes de cambiar de idioma, debe tener en cuenta que Kotlin, Scala, o lo que sea el JVM language du jour hoy en día no es y nunca será Java. Lo que esté haciendo en segundo plano para hacer que las cosas funcionen puede que no funcione cuando se produzca la próxima versión de la JVM.
Las personas que mantienen el JDK en Oracle tienen un historial de uso de estos lenguajes JVM de terceros como investigación de mercado. Si una característica de alto nivel se agrega a un lenguaje JVM de terceros y es lo suficientemente popular, lo incorporarán a Java. Esto es lo que está pasando ahora con las coroutinas. Hay un proyecto OpenJDK llamado Project Loom que apunta a agregar coroutines al lenguaje Java.
Todavía es pronto para Project Loom. Si miras críticamente la propuesta, es un desastre. Estoy seguro de que se estabilizará a medida que pase el tiempo, pero lo que finalmente obtendremos puede ser completamente diferente de lo que muchos de nosotros estamos esperando.
Para resumir, sus opciones son usar uno de los kits de herramientas de instrumentación de bytecode o cambiar de idioma. Project Loom aún está en sus inicios y existe la posibilidad de que nunca se agregue a Java.
Sus requerimientos parecen ser:
- ligero - no basado en hilos,
- no confiar en el código nativo, y
- no se utiliza modificación de bytecode.
Tengo la desagradable sensación de que estos requisitos han descartado todas las estrategias sensatas para implementar coroutines en Java.
Javaflow es una implementación de continuación, probablemente te permita hacerlo. Sin embargo, utiliza la manipulación de bytecode.
De todos modos, parece que estás tratando de hacer OOP con C simple. Es factible, pero no significa que debas hacerlo.
Quasar implementa coroutines y canales tipo Go , entre otras características, utilizando continuaciones.
Más detalles, puntos de referencia y enlaces en Quasar en mi otra respuesta .