unlp programacion paralela ejemplos concurrente scala concurrency go

scala - programacion - ¿Cómo funcionan mejor los lenguajes concurrentes de transmisión de mensajes que los lenguajes concurrentes de memoria compartida en la práctica?



programacion concurrente unlp (4)

En un programa Go idiomático, los hilos comunican estado y datos a través de canales. Esto se puede hacer sin la necesidad de bloqueos. Pasar datos a través de un canal a un receptor implica la transferencia de la propiedad de los datos. Una vez que envíe un valor a través de un canal, ya no debería estar trabajando en él, ya que quien lo recibió ahora lo ''posee''.

Sin embargo, debe tenerse en cuenta que esta transferencia de ''propiedad'' no se aplica de ninguna manera por el tiempo de ejecución de Go. Los objetos enviados a través de canales no están marcados o marcados ni nada por el estilo. Es meramente una convención. Para que pueda, si le apetece, dispararse en el pie al mutar un valor que previamente envió a través de un canal.

La fuerza de Go radica en que la sintaxis que ofrece Go (lanzamiento de goroutines y la forma en que funcionan los canales) hace que sea mucho más fácil escribir código que funcione correctamente y así evitar condiciones de carrera y bloqueos muertos. La mecánica de concurrencia clara de Go hace que sea muy fácil razonar sobre lo que sucederá en su programa.

Como nota al margen: la biblioteca estándar en Go aún ofrece los mutexes y semáforos tradicionales si realmente desea usarlos. Pero obviamente lo haces a tu propia discreción y riesgo.

He sido desarrollador de Java durante muchos años, pero nunca tuve que lidiar demasiado con los problemas de concurrencia hasta que comencé a desarrollar Android, y de repente comencé a encontrar situaciones en las que la "aplicación no respondía" y aparente interbloqueo.

Esto me hizo darme cuenta de lo difícil que puede ser comprender y depurar algunos de estos problemas de concurrencia. ¿Cómo mejoran los idiomas nuevos, como Scala y Go, la concurrencia? ¿Cómo son más comprensibles y cómo previenen errores de concurrencia? ¿Puede alguien proporcionar ejemplos del mundo real que demuestren las ventajas?


Los actores de Scala trabajan en un principio de nada compartido, por lo que no hay bloqueos (¡y por lo tanto no hay puntos muertos)! Los actores escuchan los mensajes y son invocados por el código que tiene algo sobre lo que trabajar un actor.


Los tres principales contendientes para simplificar la concurrencia son los actores, la memoria transaccional de software (STM) y la paralelización automática. Scala tiene implementaciones de los tres.

Actores

Los actores encuentran su implementación más notable en el lenguaje Erlang, que hasta donde sé es dónde comenzó la idea *. Erlang está diseñado desde cero alrededor de los actores. La idea es que los propios actores son cajas negras entre sí; solo interactúan pasando mensajes.

Scala tiene una implementación de actores en su biblioteca, y las variantes están disponibles en bibliotecas externas. En la biblioteca principal, el black-box-ness no se aplica, pero hay métodos fáciles de usar para pasar mensajes, y Scala facilita la creación de mensajes inmutables (para que no tenga que preocuparse de que envíe un mensaje). mensaje con un poco de contenido, y luego cambie el contenido en algún momento al azar).

La ventaja de los actores es que no tienen que preocuparse por el estado compartido complejo, lo que realmente simplifica el razonamiento involucrado. Además, puede descomponer el problema en partes más pequeñas que los hilos y dejar que la biblioteca del actor descubra cómo agrupar actores en la cantidad adecuada de hilos.

La desventaja es que si intenta hacer algo complejo, tiene que lidiar con mucha lógica para enviar mensajes, manejar errores, etc., antes de darse cuenta de que tiene éxito.

Memoria transaccional de software

STM se basa en la idea de que la operación concurrente más importante es tomar algún estado compartido, manipularlo y volver a escribirlo. Por lo tanto, proporciona un medio para hacer esto; sin embargo, si encuentra algún problema, que por lo general demora la detección hasta el final, en cuyo punto comprueba que todas las escrituras fueron correctas, revierte los cambios y devuelve un error (o lo intenta de nuevo).

Esto es de alto rendimiento (en situaciones con contención moderada, ya que generalmente todo va bien) y robusto para la mayoría de los tipos de errores de bloqueo , ya que el sistema STM puede detectar problemas (e incluso potencialmente hacer cosas como quitar acceso de un menor -prueba de prioridad y darle a uno de mayor prioridad).

A diferencia de los actores, es más fácil intentar cosas complejas, siempre y cuando puedas manejar el error. Sin embargo, también debe razonar correctamente sobre el estado subyacente; STM evita bloqueos no intencionales raros a través de fallas y reintentos, pero si simplemente ha cometido un error de lógica y no se puede completar un cierto conjunto de pasos, STM no puede permitírselo.

Scala tiene una biblioteca STM que no es parte de la biblioteca estándar, pero se está considerando su inclusión. Clojure y Haskell tienen bibliotecas STM bien desarrolladas.

Paralelización automática

La paralelización automática considera que no se debe pensar en la concurrencia; solo quieres que las cosas sucedan rápido. Por lo tanto, si tiene algún tipo de operación paralela, por ejemplo, aplicando alguna operación compleja a una colección de elementos, uno por vez, y produciendo alguna otra colección como resultado, debe tener rutinas que hagan esto automáticamente en paralelo. Las colecciones de Scala se pueden usar de esta manera (hay un método .par que convierte una colección en serie convencional en su análogo paralelo). Muchos otros lenguajes tienen características similares (Clojure, Matlab, etc.).

Editar: En realidad, el modelo Actor fue descrito en 1973 y probablemente fue motivado por un trabajo anterior en Simula 67 (usando corutinas en lugar de concurrencia); en 1978 vinieron los procesos secuenciales de comunicación relacionados. Por lo tanto, las capacidades de Erlang no eran únicas en ese momento, pero el lenguaje fue excepcionalmente efectivo para implementar el modelo de actor.


Para mí, usar actores de Scala (Akka) ha tenido varias ventajas sobre los modelos de concurrencia tradicionales:

  1. El uso de un sistema de transmisión de mensajes como actores le brinda una manera de manejar fácilmente el estado compartido. Por ejemplo, con frecuencia incluiré una estructura de datos mutable en un actor, por lo que la única forma de acceder a ella es mediante el envío de mensajes. Como los actores siempre procesan un mensaje a la vez, esto garantiza que todas las operaciones en los datos sean seguras para la ejecución de subprocesos.
  2. Los actores eliminan parcialmente la necesidad de lidiar con el desove y el mantenimiento de los hilos. La mayoría de las bibliotecas de actores se encargarán de distribuir a los actores entre los hilos, por lo que solo tendrá que preocuparse por iniciar y detener a los actores. A menudo crearé una serie de actores idénticos, uno por núcleo físico de CPU, y usaré un agente equilibrador de carga para distribuirles mensajes de manera uniforme.
  3. Los actores pueden ayudar a mejorar la confiabilidad del sistema. Uso actores de Akka, y una característica es que puedes crear un supervisor para los actores, donde si un actor falla, el supervisor creará automáticamente una nueva instancia. esto puede ayudar a prevenir situaciones con subprocesos donde un hilo se cuelga y está atascado con un programa medio ejecutando. También es muy fácil crear nuevos actores según sea necesario y trabajar con actores remotos que se ejecutan en otra aplicación.

Aún necesita una comprensión decente de la concurrencia y la programación de subprocesos múltiples, ya que los bloqueos y las condiciones de carrera todavía son posibles, pero los actores hacen que sea mucho más fácil identificar y resolver estos problemas. No sé cuánto se aplican a las aplicaciones de Android, pero la mayoría de las veces lo hago desde el servidor y el uso de actores ha hecho que el desarrollo sea mucho más fácil.