2013-08-11 24 views
8

Sto cercando di imparare il multiprocessing di Python.Esempio di documentazione di multiprocessing di Python

http://docs.python.org/2/library/multiprocessing.html dall'esempio di "Per mostrare gli ID di processo individuale coinvolti, ecco un esempio esteso:"

from multiprocessing import Process 
import os 

def info(title): 
    print title 
    print 'module name:', __name__ 
    if hasattr(os, 'getppid'): # only available on Unix 
     print 'parent process:', os.getppid() 
    print 'process id:', os.getpid() 

def f(name): 
    info('function f') 
    print 'hello', name 

if __name__ == '__main__': 
    info('main line') 
    p = Process(target=f, args=('bob',)) 
    p.start() 
    p.join() 

Che cosa sto guardando? Vedo che def f (nome): viene chiamato dopo l'informazione ('linea principale') è finito, ma questa chiamata sincrona sarebbe comunque predefinita. Vedo che le stesse informazioni sul processo ('linea principale') sono il PID genitore di def f (nome): ma non sono sicuro di cosa sia il 'multiprocessing' a riguardo.

Inoltre, con join() "Blocca il thread chiamante fino a quando il processo il cui metodo join() viene chiamato termina". Non sono chiaro su quale sarebbe il thread chiamante. In questo esempio cosa sarebbe join() da bloccare?

risposta

25

Come multiprocessing opere, in poche parole:

  • Process() depone le uova (fork o simili su sistemi Unix-like) una copia del programma originale (su Windows, che manca di un vero e proprio fork, questo è difficile e richiede la cura speciale che la documentazione del modulo rileva).
  • La copia comunica con l'originale per capire che (a) è una copia e (b) dovrebbe spegnersi e richiamare la funzione target= (vedere sotto).
  • A questo punto, l'originale e la copia sono ora diversi e indipendenti e possono essere eseguiti contemporaneamente.

Poiché si tratta di processi indipendenti, che hanno ora globali Interprete serrature indipendenti (in CPython) così entrambi possono utilizzare fino al 100% di una CPU in un box multi-CPU, purché non contendono altre risorse di livello inferiore (SO). Questa è la parte "multiprocessing".

Ovviamente, a un certo punto è necessario inviare dati avanti e indietro tra questi processi presumibilmente indipendenti, ad esempio per inviare i risultati da uno (o più) processi di lavoro a un processo "principale". (C'è un'eccezione occasionale in cui ognuno è completamente indipendente, ma è raro ... in più c'è l'intera sequenza di avvio stessa, avviata da p.start().) Quindi ogni istanza creata Process- p nell'esempio precedente-ha un canale di comunicazione al suo genitore creatore e viceversa (è una connessione simmetrica). Il modulo multiprocessing utilizza il modulo pickle per trasformare i dati in stringhe, le stesse stringhe che è possibile memorizzare nei file con pickle.dump, e invia i dati attraverso il canale, "verso il basso" ai lavoratori per inviare argomenti e tali e "verso l'alto" dai lavoratori a inviare i risultati.

Alla fine, una volta che hai finito con i risultati, l'operatore termina (tornando dalla funzione target=) e dice al genitore che è fatto. Per assicurarsi che tutto venga chiuso e ripulito, il genitore deve chiamare p.join() per attendere il messaggio "I'm done" del lavoratore (in realtà un OS-level exit su Unix-ish sysems).

L'esempio è un po 'sciocco poiché i due messaggi stampati non richiedono praticamente alcun tempo, quindi eseguirli "allo stesso tempo" non ha alcun guadagno misurabile. Ma supponiamo che invece di stampare semplicemente hello, f dovessimo calcolare le prime 100.000 cifre di π (3.14159 ...). È quindi possibile generare un altro Process, p2 con un altro target g che calcola le prime 100.000 cifre di e (2.71828 ...). Questi funzionerebbero indipendentemente.Il genitore potrebbe quindi chiamare p.join() e p2.join() per attendere che entrambi completino (o generino ancora più lavoratori per fare più lavoro e occupare più CPU, o anche andare avanti e fare il proprio lavoro per un po 'prima).