2013-05-08 3 views
15

Ich habe den folgenden Code, der die MD5-Summen in eine LogdateiPython: Laufen subprocess parallel

for file in files_output: 
    p=subprocess.Popen(['md5sum',file],stdout=logfile) 
p.wait() 
  1. Wird diese parallel geschrieben werden, schreibt? Wenn md5sum eine lange Zeit für eine der Dateien benötigt, wird eine andere gestartet, bevor auf die Fertigstellung einer vorherigen Datei gewartet wird?

  2. Wenn die Antwort zu obigem ja ist, kann ich annehmen, dass die Reihenfolge der md5sums, die in logfile geschrieben werden, davon abhängen kann, wie lange md5sum für jede Datei dauert? (einige Dateien können riesig sein, einige klein)

Antwort

14

Alle Unterprozesse werden parallel ausgeführt. (Um dies zu vermeiden, muss man explizit auf ihren Abschluss warten.) Sie können sogar gleichzeitig in die Protokolldatei schreiben und so die Ausgabe verstopfen. Um dies zu vermeiden, sollten Sie jeden Prozess in eine andere Protokolldatei schreiben und alle Ausgaben erfassen, wenn alle Prozesse abgeschlossen sind.

q = Queue.Queue() 
result = {} # used to store the results 
for fileName in fileNames: 
    q.put(fileName) 

def worker(): 
    while True: 
    fileName = q.get() 
    if fileName is None: # EOF? 
     return 
    subprocess_stuff_using(fileName) 
    wait_for_finishing_subprocess() 
    checksum = collect_md5_result_for(fileName) 
    result[fileName] = checksum # store it 

threads = [ threading.Thread(target=worker) for _i in range(20) ] 
for thread in threads: 
    thread.start() 
    q.put(None) # one EOF marker for each thread 

Danach sollten die Ergebnisse in result gespeichert werden.

+0

Vielen Dank. Allerdings habe ich 1000 von md5sums. Ich würde lieber keine separate Datei für jeden öffnen. – imagineerThat

+2

Nein, sollten Sie nicht. Erstellen Sie eine 'Queue.Queue' und einen Thread-Pool von einigen Dutzend Threads, lassen Sie jeden Thread ein Element aus der Warteschlange lesen und starten Sie einen Subprozess für dieses Element, warten Sie auf den Abschluss dieses Subprozesses, holen Sie sich das Ergebnis (die md5-Checksumme) , speichern Sie das Ergebnis in einem Mapping. Wenn die Warteschlange leer ist, sollten die Threads beendet werden. – Alfe

+0

Neu bei Python noch. Muss ich Queue.Queue verwenden, um gleichzeitig in ein Mapping zu schreiben? Wenn nicht, was macht Queue.Queue für mich? – imagineerThat

19
  1. Ja, diese md5sum-Prozesse werden parallel gestartet.
  2. Ja, die Reihenfolge der MD5sums Schreibvorgänge wird unvorhersehbar sein. Und im Allgemeinen wird es als eine schlechte Praxis angesehen, eine einzelne Ressource wie Datei von vielen Prozessen auf diese Weise zu teilen.

auch Ihr Weg p.wait() nach der for Schleife machen wird nur für die letzten von md5sum Prozessen warten zu beenden und der Rest von ihnen noch ausgeführt werden könnte.

Aber Sie können diesen Code leicht ändern, um noch Vorteile der parallelen Verarbeitung und Vorhersagbarkeit der synchronisierten Ausgabe zu haben, wenn Sie die md5sum Ausgabe in temporäre Dateien sammeln und wieder in einer Datei sammeln, sobald alle Prozesse abgeschlossen sind.

import subprocess 
import os 

processes = [] 
for file in files_output: 
    f = os.tmpfile() 
    p = subprocess.Popen(['md5sum',file],stdout=f) 
    processes.append((p, f)) 

for p, f in processes: 
    p.wait() 
    f.seek(0) 
    logfile.write(f.read()) 
    f.close() 
+0

Also ich denke, die Reihenfolge hier ist erhalten, weil Prozesse [] verfolgt es? dh. process.append ((p, f)) wird ausgeführt, bevor md5sum in der Reihenfolge von files_output endet. – imagineerThat

+2

Ja, 'processes []' behält die ursprüngliche Reihenfolge von 'files_output []' und stellt sicher, dass jeder md5sum-Prozess beendet ist. Aber wenn Sie sich Gedanken über die Ressourcen von OS machen, sollten Sie den Thread-Pool mit Task-Queue und synchrone md5sum in jedem Thread mit 'subprocess.check_output()' ausführen, wie es @Alfe vorgeschlagen hat. – dkz

5

Ein einfacher Weg, Ausgabe von parallel md5sum Subprozesse zu sammeln ist ein Thread-Pool zu verwenden und aus dem Hauptprozess in die Datei schreiben:

from multiprocessing.dummy import Pool # use threads 
from subprocess import check_output 

def md5sum(filename): 
    try: 
     return check_output(["md5sum", filename]), None 
    except Exception as e: 
     return None, e 

if __name__ == "__main__": 
    p = Pool(number_of_processes) # specify number of concurrent processes 
    with open("md5sums.txt", "wb") as logfile: 
     for output, error in p.imap(md5sum, filenames): # provide filenames 
      if error is None: 
       logfile.write(output) 
  • die Ausgabe von md5sum klein ist, dass Sie so kann es im Speicher speichern
  • imap Konserven Reihenfolge
  • number_of_processes kann sich von der Anzahl der Dateien oder der CPU unterscheiden Kerne (größere Werte bedeutet nicht schneller: es hängt von der relativen Leistung von IO (Festplatten) und CPU)

Sie können versuchen, mehrere Dateien auf einmal zu den Unterprozessen md5sum übergeben.

In diesem Fall benötigen Sie keinen externen Subprozess; you can calculate md5 in Python:

import hashlib 
from functools import partial 

def md5sum(filename, chunksize=2**15, bufsize=-1): 
    m = hashlib.md5() 
    with open(filename, 'rb', bufsize) as f: 
     for chunk in iter(partial(f.read, chunksize), b''): 
      m.update(chunk) 
    return m.hexdigest() 

mehr Prozesse anstelle von Threads zu verwenden (md5sum() dem reinen Python zu ermöglichen läuft parallel Verwendung mehrerer CPUs) in dem obigen Code nur Drop .dummy aus dem Import.

+0

Sorry, lerne immer noch hier. Ich verstehe nicht, warum Warteschlangen hier nicht verwendet wird. Wenn mehrere Prozesse in die Protokolldatei schreiben, gibt es keine Probleme? Wenn ich mich irre, wie läuft die Synchronisation ab? – imagineerThat

+0

Sieht aus wie 'Pool' asynchrone Aufrufe unterstützt. Bedeutet dies, dass es die Reihenfolge der geschriebenen md5 (in der Reihenfolge der 'Dateinamen') beibehält? Anders als einfach x Anzahl der Threads starten? – imagineerThat

+1

'Pool' bietet eine höhere Schnittstelle. Es benutzt 'Queue's selbst intern. Auf 'logfile'-Datei wird nur vom Haupt-Thread zugegriffen (nur die' md5sum() '-Funktion wird in untergeordneten Threads ausgeführt). 'imap()' gibt Ergebnisse in der Reihenfolge zurück (wie ich es bereits ausdrücklich erwähnt habe) – jfs