multithreading perl parallel-processing

multithreading - ¿Casos de uso para ithreads(hilos de intérprete) en Perl y razones para usarlos o no?



parallel-processing (2)

He usado los "hilos" de perl en varias ocasiones. Son más útiles para iniciar un proceso y continuar con otra cosa. No tengo mucha experiencia en la teoría de cómo funcionan bajo el capó, pero sí tengo mucha experiencia de codificación práctica con ellos.

Por ejemplo, tengo un hilo de servidor que escucha las conexiones de red entrantes y escupe una respuesta de estado cuando alguien lo solicita. Creo ese hilo, luego sigo y creo otro hilo que monitorea el sistema, verifica cinco elementos, duerme unos segundos y vuelve a hacer bucles. Puede tomar de 3 a 4 segundos recopilar los datos del monitor, luego se inserta en una variable compartida, y el hilo del servidor puede leer eso cuando sea necesario e inmediatamente devolver el último resultado conocido a quien lo solicite. El subproceso del monitor, cuando detecta que un elemento está en mal estado, inicia una cadena separada para reparar ese elemento. Luego se mueve, verifica los otros elementos mientras se repara el defectuoso y lanza otros hilos para otros artículos defectuosos o se une a los hilos de reparación terminados. El programa principal está bucleando cada pocos segundos, asegurándose de que el monitor y los hilos del servidor no se puedan unir / seguir funcionando. Todo esto podría escribirse como un conjunto de programas separados que utilizan alguna otra forma de IPC, pero los hilos de perl lo hacen simple.

Otro lugar donde los he usado está en un generador fractal. Me gustaría dividir partes de la imagen usando algún algoritmo y luego lanzar tantos hilos como CPU tenga para hacer el trabajo. Cada uno de ellos incluía sus resultados en un solo objeto GD, lo que no causaba problemas porque cada uno de ellos trabajaba en diferentes partes de la matriz, y luego, cuando terminaba, escribía la imagen GD. Fue mi introducción al uso de subprocesos perl, y fue una buena introducción, pero luego lo reescribí en C y fue dos órdenes de magnitud más rápido :-). Luego volví a escribir mi versión de Perl para utilizar Inline :: C, y fue solo un 20% más lenta que la versión C pura. Aún así, en la mayoría de los casos en los que desearía utilizar subprocesos debido a que consume mucha CPU, probablemente quiera simplemente elegir otro idioma.

Como lo mencionaron otros, el tenedor y los hilos realmente se superponen para muchos propósitos. Sin embargo, Coro no permite el uso de varias CPU o el procesamiento en paralelo, como tenedor e hilo, solo verá su proceso usando 100%. Estoy simplificando demasiado esto, pero creo que la forma más fácil de describir a Coro es que es un programador para sus subrutinas. Si tiene una subrutina que bloquea, puede saltar a otra y hacer otra cosa mientras espera, por ejemplo, si tiene una aplicación que calcula los resultados y los escribe en un archivo. Un bloque puede calcular resultados e insertarlos en un canal. Cuando se queda sin trabajo, otro bloque comienza a escribirlos en el disco. Mientras ese bloque está esperando en el disco, el otro bloque puede comenzar a calcular los resultados nuevamente si obtiene más trabajo. Es cierto que no he hecho mucho con Coro; suena como una buena forma de acelerar algunas cosas, pero estoy un poco desconcertado por no poder hacer dos cosas a la vez.

Mi preferencia personal, si quiero hacer multiprocesamiento, es usar tenedor si estoy haciendo muchas cosas pequeñas o cortas, hilos para un puñado de cosas grandes o de larga vida.

Si desea aprender a usar los hilos de intérprete de Perl, hay una buena documentación en perlthrtut (subprocesos tutoriales) y en la página de manual de threads pragma . Definitivamente es lo suficientemente bueno para escribir algunos scripts simples.

Sin embargo, he encontrado poca guía en la web sobre por qué y para qué usar sensiblemente los hilos de intérprete de Perl. De hecho, no se habla mucho de ellos, y si la gente habla de ellos, con frecuencia es para desalentar a las personas a usarlos.

Estos hilos, disponibles cuando perl -V:useithreads es useithreads=''define''; y desatados por use threads , también se llaman ithreads , y tal vez de manera más apropiada, ya que son muy diferentes de los hilos ofrecidos por los sistemas operativos Linux o Windows o la VM de Java, ya que no se comparte nada por defecto y en cambio hay una gran cantidad de datos copiado, no solo la pila de hilos, lo que aumenta significativamente el tamaño del proceso. (Para ver el efecto, cargue algunos módulos en una secuencia de comandos de prueba, luego cree subprocesos en una pausa de bucle para presionar teclas cada vez que se encuentre, y vea aumentar la memoria en el administrador de tareas o en la top ).

[...] cada vez que inicia un hilo, todas las estructuras de datos se copian al nuevo hilo. Y cuando digo todo, quiero decir todo. Esto, por ejemplo, incluye stashes de paquetes, variables globales, léxicos en el alcance. ¡Todo!

- Cosas que debes saber antes de programar Perl ithreads (Perlmonks 2003)

Cuando investigue el tema de Perl ithreads, verá que las personas lo desaniman de usarlos ( "idea extremadamente mala", "fundamentalmente defectuosa" , o "nunca use hilos de rosca para nada" ).

El tutorial de subprocesos de Perl resalta que "los subprocesos de Perl son diferentes" , pero no se molesta en explicar cómo son diferentes y qué significa eso para el usuario.

Una explicación útil pero muy breve de lo que realmente son los hilos is es de la página de manual de Coro bajo el encabezado EMULACIÓN DEL PROCESO DE WINDOWS . El autor de ese módulo ( Coro, los únicos hilos reales en Perl ) también desalienta el uso de hilos de intérprete Perl.

En algún lugar que he leído que compilar Perl con hilos habilitados dará como resultado un intérprete significativamente más lento.

Hay una página de Perlmonks de 2003 ( Cosas que necesitas saber antes de programar Perl ithreads ), en la que el autor pregunta: "Ahora puedes preguntarte por qué Perl ithreads no usó fork () ¿No habría tenido mucho más sentido? ? " Esto parece haber sido escrito por el autor de las forks pragma. No estoy seguro de que la información que figura en esa página aún sea cierta en 2012 para los Perls más nuevos.

Aquí hay algunas pautas para el uso de subprocesos en Perl que he extraído de mis lecturas (tal vez erróneamente):

Hasta ahora mi investigación. Ahora, gracias por más luz que puede arrojar sobre este tema de hilos en Perl. ¿Cuáles son algunos casos de uso sensato para ithreads en Perl? ¿Cuál es la razón para usar o no usarlos?


La respuesta corta es que son bastante pesados ​​(no se pueden lanzar más de 100 de ellos a bajo precio), y exhiben comportamientos inesperados (algo mitigados por los recientes módulos de CPAN).

Puede usar Perl ithreads de forma segura al tratarlos como Actores independientes .

  1. Cree un Thread :: Queue :: Any para "trabajo".
  2. Ejecute varias ithreads y "result" Queues pasándolas ("work" + own "result") Queues by closure.
  3. Cargue (requiera) todo el código restante que su aplicación requiera (¡no antes de los hilos!)
  4. Agregue trabajo para los hilos en la cola según sea necesario.

En ithreads "trabajador":

  1. Trae cualquier código común (para cualquier tipo de trabajo)
  2. Bloqueo-dequeue una pieza de trabajo de la cola
  3. Demanda cargar cualquier otra dependencia requerida para este trabajo.
  4. Haz el trabajo.
  5. Pase el resultado al hilo principal a través de la cola "resultado".
  6. Volver a 2.

Si algunos subprocesos de "trabajador" comienzan a ser un poco carnosos, y debe limitar los hilos "de trabajo" a un número y luego lanzarlos en su lugar, primero cree un subproceso "iniciador", cuyo trabajo es iniciar "trabajador" "hilos y conectarlos al hilo principal.

¿Cuáles son los principales problemas con Perl ithreads?

Son un poco incómodos con los datos "compartidos", ya que es necesario que explícitamente compartan (no es un gran problema).

Debe tener en cuenta el comportamiento de los objetos con los métodos DESTROY a medida que se salen del alcance de un hilo (¡si todavía son necesarios en otro)!

El más grande : los datos / variables que no se comparten explícitamente son CLONADOS en nuevos hilos. Este es un golpe de rendimiento y probablemente no sea lo que pretendías. La solución alternativa es lanzar ithreads desde una condición bastante "prístina" (no muchos módulos cargados).

IIRC, hay módulos en el espacio de nombres Threads :: que ayudan a hacer que las dependencias sean explícitas y / o limpiar datos clonados para nuevos hilos.

Además, IIRC, hay un modelo ligeramente diferente que utiliza ithreads llamados hilos "Apartamento", implementado por Thread :: Appartment que tiene un patrón de uso diferente y otro conjunto de compensaciones.

El resultado:

No los uses a menos que sepas lo que estás haciendo :-)

Fork puede ser más eficiente en Unix, pero la historia de IPC es mucho más simple para ithreads. (Esto puede haber sido mitigado por los módulos de CPAN desde la última vez que miré :-)

Todavía son mejores que los hilos de Python.

Es posible que, algún día, sea algo mucho mejor en Perl 6.