pyplot python multithreading python-stackless

pyplot - subplot python title



Enhebrado en Python (7)

Con respecto a Kamaelia, la respuesta anterior no cubre el beneficio aquí. El enfoque de Kamaelia proporciona una interfaz unificada, que es pragmática, no perfecta, para tratar con hilos, generadores y procesos en un solo sistema de concurrencia.

Fundamentalmente proporciona una metáfora de una cosa en ejecución que tiene bandeja de entrada y bandeja de salida. Envía mensajes a las bandejas de salida, y cuando se conectan juntos, los mensajes fluyen de las bandejas de salida a las bandejas de entrada. Esta metáfora / API sigue siendo la misma ya sea que esté usando generadores, hilos o procesos, o hablando con otros sistemas.

La parte "no perfecta" se debe a que el azúcar sintáctico no se ha agregado aún para las bandejas de entrada y las de salida (aunque esto se está discutiendo): hay un enfoque en la seguridad / usabilidad en el sistema.

Tomando el ejemplo del consumidor productor usando el subproceso desnudo anterior, esto se convierte en esto en Kamaelia:

Pipeline(Producer(), Consumer() )

En este ejemplo, no importa si se trata de componentes con hebras o de lo contrario, la única diferencia entre ellos es desde la perspectiva de uso la clase base para el componente. Los componentes del generador se comunican mediante listas, componentes enhebrados utilizando Queue.Queues y procesos basados ​​en os.pipes.

Sin embargo, la razón detrás de este enfoque es dificultar la tarea de depurar errores. Al enhebrar - o cualquier concurrencia de memoria compartida que tenga, el problema número uno que enfrenta es accidentalmente roto las actualizaciones de datos compartidos. Al usar el mensaje que pasa, elimina una clase de errores.

Si usa el subprocesamiento sin hilos y los bloqueos en todas partes, generalmente está trabajando suponiendo que al escribir el código no cometerá ningún error. Aunque todos aspiramos a eso, es muy raro que eso suceda. Al concluir el comportamiento de bloqueo en un lugar, simplifica las cosas que pueden salir mal. (Los manejadores de contexto ayudan, pero no ayudan con las actualizaciones accidentales fuera del controlador de contexto)

Obviamente, no todos los códigos se pueden escribir como mensajes y estilos compartidos, por lo que Kamaelia también tiene una simple memoria transaccional de software (STM), que es una idea genial con un nombre desagradable, es más como control de versiones para variables, es decir verifique algunas variables, actualícelas y vuelva a comprometerse. Si tienes un choque, enjuaga y repite.

Enlaces relevantes:

De todos modos, espero que sea una respuesta útil. FWIW, la razón principal detrás de la configuración de Kamaelia es hacer que la concurrencia sea más segura y fácil de usar en los sistemas de pitón, sin que la cola se mueva de miedo. (es decir, el gran cubo de componentes

Puedo entender por qué la otra respuesta de Kamaelia fue modificada, ya que incluso para mí parece más un anuncio que una respuesta. Como autor de Kamaelia, es agradable ver entusiasmo, aunque espero que contenga un contenido un poco más relevante :-)

Y esa es mi forma de decir, por favor, tengan la advertencia de que esta respuesta es, por definición, parcial, pero para mí, el objetivo de Kamaelia es tratar de ajustar lo que es la mejor práctica de la OMI. Sugeriría probar algunos sistemas y ver cuál funciona para usted. (también si esto no es apropiado para el desbordamiento de pila, lo siento, soy nuevo en este foro :-)

¿Cuáles son los módulos utilizados para escribir aplicaciones multiproceso en Python? Soy consciente de los mecanismos de concurrencia básicos proporcionados por el lenguaje y también de Python de Stackless , pero ¿cuáles son sus fortalezas y debilidades respectivas?


Depende de lo que intentes hacer, pero soy parcial al solo usar el módulo de threading en la biblioteca estándar porque hace que sea muy fácil tomar cualquier función y simplemente ejecutarla en una secuencia separada.

from threading import Thread def f(): ... def g(arg1, arg2, arg3=None): .... Thread(target=f).start() Thread(target=g, args=[5, 6], kwargs={"arg3": 12}).start()

Y así. A menudo tengo una configuración de productor / consumidor usando una cola sincronizada provista por el módulo Queue

from Queue import Queue from threading import Thread q = Queue() def consumer(): while True: print sum(q.get()) def producer(data_source): for line in data_source: q.put( map(int, line.split()) ) Thread(target=producer, args=[SOME_INPUT_FILE_OR_SOMETHING]).start() for i in range(10): Thread(target=consumer).start()


En orden de complejidad creciente:

Use el módulo de roscado

Pros:

  • Es realmente fácil ejecutar cualquier función (cualquier invocable de hecho) en su propio hilo.
  • Compartir datos es, si no fácil (los bloqueos nunca son fáciles :), al menos simples.

Contras:

  • Como mencionan Juergen Python, los hilos no pueden acceder simultáneamente al estado en el intérprete (hay un gran bloqueo, el infame Global Interpreter Lock ). Lo que eso significa en la práctica es que los subprocesos son útiles para tareas vinculadas de E / S (creación de redes, escritura en disco, y así sucesivamente), pero no del todo útil para hacer cálculos concurrentes.

Use el módulo de multiprocessing

En el caso de uso simple, esto se ve exactamente como usar el threading excepto que cada tarea se ejecuta en su propio proceso, no en su propio subproceso. (Casi literalmente: si toma el ejemplo de Eli y reemplaza el threading con multiprocessing , Thread , con Process y Queue (el módulo) con multiprocessing.Queue , debería ejecutarse correctamente).

Pros:

  • Simultaneidad real para todas las tareas (sin bloqueo de intérprete global).
  • Escala a múltiples procesadores, incluso puede escalar a varias máquinas .

Contras:

  • Los procesos son más lentos que los hilos.
  • El intercambio de datos entre procesos es más complicado que con los hilos.
  • La memoria no está implícitamente compartida. Tienes que compartirlo explícitamente o tienes que agrupar las variables y enviarlas de un lado a otro. Esto es más seguro, pero más difícil. (Si es importante, los desarrolladores de Python parecen estar presionando a la gente en esta dirección).

Use un modelo de evento, como Twisted

Pros:

  • Obtienes un control extremadamente fino sobre la prioridad, más de lo que se ejecuta cuando.

Contras:

  • Incluso con una buena biblioteca, la programación asincrónica suele ser más difícil que la programación con subprocesos, difícil tanto en términos de comprensión de lo que se supone que debe suceder como en términos de depuración de lo que realmente está sucediendo.

En todos los casos, supongo que ya comprende muchos de los problemas relacionados con la multitarea, específicamente la complicada cuestión de cómo compartir datos entre tareas. Si por alguna razón no sabe cuándo y cómo usar bloqueos y condiciones, debe comenzar por ellos. El código multitarea está lleno de sutilezas y errores, y es mejor tener una buena comprensión de los conceptos antes de comenzar.


Si realmente quieres ensuciarte las manos, puedes intentar usar generadores para corotines falsas . Probablemente no sea el más eficiente en términos de trabajo involucrado, pero las corutinas sí le ofrecen un control muy preciso de la multitarea cooperativa en lugar de la multitarea preventiva que encontrará en otros lugares.

Una ventaja que encontrarás es que, en general, no necesitarás bloqueos o mutexes cuando uses la multitarea cooperativa, pero la ventaja más importante para mí fue la velocidad de conmutación casi nula entre "hilos". Por supuesto, se dice que Stackless Python también es muy bueno para eso; y luego está Erlang, si no tiene que ser Python.

Probablemente la mayor desventaja en la multitarea cooperativa es la falta general de solución para bloquear la E / S. Y en las corotines falsificadas, también encontrarás el problema de que no puedes cambiar los "hilos" de cualquier cosa que no sea el nivel superior de la pila dentro de un hilo.

Después de que haya realizado una aplicación incluso ligeramente compleja con corotines falsas, realmente comenzará a apreciar el trabajo que entra en la programación de procesos en el nivel del sistema operativo.


Usaría Microthreads (Tasklets) de Stackless Python, si tuviera que usar hilos.

Todo un juego en línea (multijugador masivo) se basa en Stackless y su principio de subprocesamiento múltiple, ya que el original es solo lento para la propiedad multijugador masivo del juego.

Los hilos en CPython son ampliamente desaconsejados. Una razón es el GIL, un bloqueo de intérprete global, que serializa el enhebrado de muchas partes de la ejecución. Mi experiencia es que es realmente difícil crear aplicaciones rápidas de esta manera. Mis codificaciones de ejemplo son más lentas con el subprocesamiento, con un núcleo (pero muchas esperas de entrada deberían haber hecho posibles aumentos de rendimiento).

Con CPython, mejor utilice procesos separados si es posible.


Ya has obtenido una gran variedad de respuestas, desde "hilos falsos" hasta marcos externos, pero no he visto a nadie mencionar Queue.Queue : la "salsa secreta" de enhebrar CPython.

Para expandir: siempre que no sea necesario superponer el procesamiento de CPU pura de Python (en cuyo caso necesita multiprocessing , pero también viene con su propia implementación de Queue , de modo que puede con algunas precauciones necesarias aplicar el general consejo que estoy dando ;-), el threading integrado de Python servirá ... pero lo hará mucho mejor si lo usa con conocimiento , por ejemplo, de la siguiente manera.

"Olvídate" de la memoria compartida, supuestamente la principal ventaja de la tecnología de subprocesamiento frente al multiprocesamiento: no funciona bien, no escala bien, nunca lo ha hecho, nunca lo hará. Utilice la memoria compartida solo para las estructuras de datos que se configuran una vez antes de engendrar subtítulos y nunca más después. Para todo lo demás, haga que un solo hilo sea responsable de ese recurso y comuníquese con ese hilo mediante Queue .

Dedique un hilo especializado a cada recurso que normalmente pensaría proteger mediante bloqueos: una estructura de datos mutable o grupo cohesivo del mismo, una conexión a un proceso externo (un DB, un servidor XMLRPC, etc.), un archivo externo, etc., etc. Obtenga un pequeño grupo de subprocesos para tareas de propósito general que no tengan o no necesiten un recurso dedicado de ese tipo: no genere subprocesos cuando sea necesario o la sobrecarga de cambio de subproceso lo abrumará.

La comunicación entre dos subprocesos se realiza siempre a través de Queue.Queue , una forma de paso de mensajes, la única base sensata para el multiprocesamiento (además de la memoria transaccional, que es prometedora pero no conozco ninguna implementación productiva excepto In Haskell).

Cada subproceso dedicado que gestiona un solo recurso (o un pequeño conjunto cohesivo de recursos) escucha las solicitudes en una instancia específica Queue.Queue. Los subprocesos de un grupo esperan en una única Queue.Queue compartida (Queue es sólidamente seguro para los hilos y no le fallará en esto).

Los subprocesos que solo necesitan poner en cola una solicitud en alguna cola (compartida o dedicada) lo hacen sin esperar resultados, y continúan. Hilos que finalmente necesitan un resultado o confirmación para una cola de solicitud de un par (solicitud, cola de recepción) con una instancia de Queue.Queue que acaban de crear, y finalmente, cuando la respuesta o confirmación es indispensable para proceder, se ponen (esperando ) desde su cola receptora. Asegúrese de estar listo para recibir respuestas de error, así como respuestas reales o confirmaciones (los deferred de Twisted son excelentes para organizar este tipo de respuesta estructurada, ¡por cierto!).

También puede usar Cola para "aparcar" instancias de recursos que pueden ser utilizados por un hilo, pero nunca compartirse entre varios hilos al mismo tiempo (conexiones DB con algunos componentes DBAPI, cursores con otros, etc.) - esto le permite relajarse el requisito de subproceso dedicado a favor de una mayor agrupación (un subproceso de grupo que obtiene de la cola compartida una solicitud que necesita un recurso queueable obtendrá ese recurso de la cola de aplicación apropiada, esperando si es necesario, etc., etc.).

Twisted es en realidad una buena forma de organizar este minueto (o square dance según sea el caso), no solo gracias a diferidos sino por su arquitectura base sólida, sólida y altamente escalable: puedes organizar cosas para usar subprocesos o subprocesos solo cuando realmente garantizado, mientras se hace la mayoría de las cosas que normalmente se consideran útiles para subprocesos en un único hilo dirigido por eventos.

Pero me doy cuenta de que Twisted no es para todo el mundo: "dedicar o agrupar recursos, usar Cola en el wazoo, nunca hacer nada que necesite un bloqueo o, Guido no lo permita, cualquier procedimiento de sincronización aún más avanzado, como semáforo o condición" puede seguir utilizándose incluso si no puede entender bien las metodologías sincronizadas basadas en eventos, y aún así ofrecerá más confiabilidad y rendimiento que cualquier otro enfoque de enhebrado ampliamente aplicable con el que haya tropezado.


Kamaelia es un framework Python para construir aplicaciones con muchos procesos de comunicación.

http://www.kamaelia.org/cat-trans-medium.png Kamaelia - Concurrencia útil, divertida

En Kamaelia construyes sistemas a partir de componentes simples que se comunican entre sí . Esto acelera el desarrollo, ayuda de forma masiva al mantenimiento y también significa que crea software concurrente de forma natural . Está destinado a ser accesible por cualquier desarrollador, incluidos los principiantes. También lo hace divertido :)

¿Qué tipo de sistemas? Servidores de red, clientes, aplicaciones de escritorio, juegos basados ​​en pygame, sistemas y tuberías de transcodificación, sistemas de televisión digital, borradoras de correo no deseado, herramientas de enseñanza, y mucho más :)

Aquí hay un video de Pycon 2009. Comienza comparando Kamaelia con Twisted y Parallel Python y luego ofrece una demostración práctica de Kamaelia.

Fácil concurrencia con Kamaelia - Parte 1 (59:08)
Fácil concurrencia con Kamaelia - Parte 2 (18:15)