2012-09-18 32 views
7

No entiendo por qué Pipes se dicen inseguros cuando hay varios remitentes y receptores.¿Por qué el tubo de multiprocesamiento Python no es seguro?

¿Cómo se puede convertir el siguiente código en código usando Queues si este es el caso? Queues no arroje EOFError cuando está cerrado, por lo que mis procesos no pueden detenerse. ¿Debo enviar mensajes interminables de 'Veneno' para decirles que se detengan (de esta manera, estoy seguro de que todos mis procesos reciben al menos un veneno)?

Me gustaría mantener la tubería p1 abierta hasta que decida lo contrario (aquí es cuando he enviado los 10 mensajes).


from multiprocessing import Pipe, Process 
from random import randint, random 
from time import sleep 

def job(name, p_in, p_out): 
    print(name + ' starting') 
    nb_msg = 0 
    try: 
     while True: 
      x = p_in.recv() 
      print(name + ' receives ' + x) 
      nb_msg = nb_msg + 1 
      p_out.send(x) 
      sleep(random()) 
    except EOFError: 
     pass 
    print(name + ' ending ... ' + str(nb_msg) + ' message(s)') 

if __name__ == '__main__': 
    p1_in, p1_out = Pipe() 
    p2_in, p2_out = Pipe() 

    proc = [] 

    for i in range(3): 
     p = Process(target=job, args=(str(i), p1_out, p2_in)) 
     p.start() 
     proc.append(p) 

    for x in range(10): 
     p1_in.send(chr(97+x)) 
    p1_in.close() 
    for p in proc: 
     p.join() 
    p1_out.close() 
    p2_in.close() 

    try: 
     while True: 
      print(p2_out.recv()) 
    except EOFError: 
     pass 

    p2_out.close() 

Respuesta

13

Esencialmente, el problema es que Pipe es una envoltura delgada alrededor de un objeto tubo de plataforma-definido. recv simplemente recibe repetidamente un búfer de bytes hasta que se obtiene un objeto completo de Python. Si dos hilos o procesos utilizan recv en la misma tubería, las lecturas pueden intercalarse, dejando cada proceso con la mitad de un objeto en escabeche y, por lo tanto, corromper los datos. Queue s realizan una sincronización adecuada entre los procesos, a expensas de una mayor complejidad.

Como la documentación multiprocessing pone:

Tenga en cuenta que los datos de una tubería pueden dañarse si dos procesos (o hilos) intentan leer o escribir en el mismo extremo de la tubería al mismo tiempo, . Por supuesto, no hay riesgo de corrupción en los procesos que utilizan diferentes extremos de la tubería al mismo tiempo.

No tiene que enviar interminablemente píldoras venenosas; uno por trabajador es todo lo que necesita. Cada trabajador recoge exactamente una píldora venenosa antes de salir, por lo que no hay peligro de que un trabajador de alguna manera pierda el mensaje.

También debería considerar el uso de multiprocessing.Pool en lugar de volver a implementar el modelo de "proceso de trabajo": Pool tiene muchos métodos que facilitan la distribución de trabajos en múltiples hilos.

+0

¿Qué ocurre si utilizo 'multiprocesamiento.Lock()' cuando uso 'recv' y' send' de una tubería? ¿Será seguro (y eficiente)? – thuzhf

+0

Si lo hace, básicamente terminará con 'Queue' -' multiprocesamiento.Queue' es un 'Pipe' con un par de bloqueos conectados (uno para cada dirección). Entonces, sería seguro y razonablemente eficiente, pero también estarías reinventando la rueda directamente, ¿por qué no usar 'Queue'? – nneonneo

7

No entiendo por qué las tuberías se dicen inseguras cuando hay varios remitentes y receptores.

Considere que pone agua en una tubería de la fuente A y B simultáneamente. En el otro extremo de la tubería, será imposible que usted sepa qué parte del agua proviene de A o B, ¿verdad? :)

Una tubería transporta una secuencia de datos en el nivel de bytes. Sin un protocolo de comunicación además, no sabe qué es un mensaje y, por lo tanto, no puede garantizar la integridad del mensaje. Por lo tanto, no solo es "inseguro" usar tuberías con múltiples remitentes. Es un defecto de diseño importante y muy probablemente dará lugar a problemas de comunicación.

Las colas, sin embargo, se implementan en un nivel superior. Están diseñados para comunicar los mensajes (o incluso objetos abstractos). Las colas se hacen para mantener un mensaje/objeto autónomo. Múltiples fuentes pueden poner objetos en una cola y múltiples consumidores pueden extraer estos objetos, al tiempo que están 100% seguros de que todo lo que entró en la cola como una unidad también sale de él como una unidad.

Edición después de un buen tiempo:

debo añadir que en el flujo de bytes, todos los bytes se recuperan en el mismo orden en que fue enviado (garantizado). El problema con múltiples remitentes es que la orden de envío (el orden de entrada) puede ser poco clara o aleatoria, es decir, múltiples flujos pueden mezclarse de forma impredecible.

Una implementación de cola común garantiza que los mensajes únicos se mantienen intactos, incluso si hay varios remitentes. Los mensajes se recuperan en el orden en que se enviaron, también. Sin embargo, con múltiples remitentes competidores y sin mecanismos de sincronización adicionales, no hay garantía sobre el orden de los mensajes de entrada.

Cuestiones relacionadas