2012-09-18 5 views
7

Non capisco perché Pipes non sia sicuro quando ci sono più mittenti e ricevitori.Perché il tubo multiprocessing Python non è sicuro?

In che modo è possibile trasformare il codice seguente in codice utilizzando Queues in questo caso? Queues non lanciare EOFError quando è chiuso, quindi i miei processi non possono fermarsi. Devo inviare infinitamente messaggi 'Veleno' per dire loro di smettere (in questo modo, sono sicuro che tutti i miei processi ricevono almeno un veleno)?

Vorrei mantenere aperta la pipe p1 finché non decido diversamente (qui è quando ho inviato i 10 messaggi).


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() 

risposta

13

Essenzialmente, il problema è che Pipe è un involucro sottile intorno a un oggetto tubo piattaforma definito. recv riceve ripetutamente un buffer di byte finché non viene ottenuto un oggetto Python completo. Se due thread o processi utilizzano recv sulla stessa pipe, le letture possono essere interlacciate, lasciando ogni processo con un mezzo oggetto decapitato e quindi corrompendo i dati. Queue eseguono la sincronizzazione corretta tra i processi, a spese di una maggiore complessità.

Come documentazione multiprocessing mette:

noti che i dati in un tubo possono venire danneggiati se due processi (o thread) tenta di leggere o scrivere alla stessa estremità del tubo contemporaneamente . Naturalmente non vi è alcun rischio di corruzione da processi che utilizzano contemporaneamente estremità diverse del tubo.

Non è necessario inviare all'infinito pillole di veleno; uno per lavoratore è tutto ciò di cui hai bisogno. Ogni lavoratore raccoglie esattamente una pillola di veleno prima di uscire, quindi non c'è pericolo che un lavoratore manchi in qualche modo il messaggio.

Si dovrebbe anche considerare l'utilizzo di multiprocessing.Pool invece di reimplementare il modello "processo di lavoro" - Pool ha molti metodi che rendono molto facile la distribuzione di lavoro su più thread.

+0

Cosa succede se uso 'multiprocessing.Lock()' quando si utilizza 'recv' e' send' di una pipe? Diventerà sicuro (ed efficiente)? – thuzhf

+0

Se lo fai, in pratica finirai con un 'Queue' -' multiprocessing.Queue' è un 'Pipe' con un paio di blocchi collegati (uno per ogni direzione). Quindi, sarebbe sicuro e ragionevolmente efficiente, ma dovresti anche reinventare direttamente la ruota - perché non usare solo "Queue"? – nneonneo

7

Non capisco perché i tubi sono detti non sicuri quando ci sono più mittenti e ricevitori.

Considera di mettere l'acqua in un tubo dalla sorgente A e B contemporaneamente. Dall'altra parte del tubo, sarà impossibile per te scoprire quale parte dell'acqua proviene da A o B, giusto? :)

Una pipe trasporta un flusso di dati a livello di byte. Senza un protocollo di comunicazione su di esso, non sa cosa sia un messaggio e quindi non può garantire l'integrità del messaggio. Pertanto, non è solo "non sicuro" utilizzare le pipe con più mittenti. È un difetto di progettazione importante e molto probabilmente porterà a problemi di comunicazione.

Le code, tuttavia, sono implementate a un livello superiore. Sono progettati per comunicare i messaggi (o anche oggetti astratti). Le code sono fatte per mantenere un messaggio/oggetto autonomo. Molteplici fonti possono mettere gli oggetti in una coda e più utenti possono estrarre questi oggetti pur essendo sicuri al 100% che qualsiasi cosa sia entrata nella coda come un'unità viene fuori come un'unità.

Modifica dopo un bel po ':

Vorrei aggiungere che nel flusso di byte, tutti i byte vengono recuperati nello stesso ordine come inviato (garantita). Il problema con più mittenti è che l'ordine di invio (l'ordine di input) potrebbe già essere poco chiaro o casuale, vale a dire che più flussi potrebbero mescolarsi in modo imprevedibile.

Un'implementazione di coda comune garantisce che i singoli messaggi siano mantenuti intatti, anche se sono presenti più mittenti. I messaggi vengono recuperati nell'ordine anche come inviati. Con più mittenti concorrenti e senza ulteriori meccanismi di sincronizzazione, tuttavia, non vi è alcuna garanzia sull'ordine dei messaggi di input.