2013-08-23 12 views
8

Was ist die richtige Lösung, um sicherzustellen, dass die Datei nie beschädigt wird, während viele Threads und Prozesse verwenden.gleichzeitige Schreiben in die gleiche Datei mit Threads und Prozesse

Version für Threads, die sich um Öffnungsfehler kümmern.

lock = threading.RLock() 
with lock: 
    try: 
    f = open(file, 'a') 
    try: 
     f.write('sth') 
    finally: 
     f.close() # try close in any circumstances if open passed 
    except: 
    pass # when open failed 

für Prozesse ich denke, muss multiprocessing.Lock

aber wenn ich zwei Prozesse werden soll, und der erste Prozess eigene 2 Threads (jeweils eine Verwendung Datei)

gibt es nur Theorie, sondern Ich möchte wissen, wie man Synchronisation mit Threads und Prozessen mischt. sind Threads "vererben" es von Prozess?, So dass nur Synchronisierung zwischen Prozessen erforderlich sind?

und 2. Ich bin mir nicht sicher, ob obiger Code verschachtelte try des Falles, dass, wenn das Schreibfehlschlagen, und wir wollen, schließen geöffnete Datei (was, wenn es nach Schloss geöffnet bleiben freigegeben)

+0

Als eine Randnotiz kann (und sollte) Ihr 'try' /' finally' durch eine 'with'-Anweisung ersetzt werden. Außerdem ist 'except: pass' in der Regel eine schlechte Idee - wenn Sie versuchen, eine bestimmte Ausnahme zu verschlucken, schlucken Sie genau diese, nicht alles. Wenn du dich hier umsiehst, wirst du Dutzende von Fragen von Leuten finden, die dumme Fehler hatten, wie zum Beispiel einen int als einen Dateinamen zu übergeben, der eine bloße Ausnahme davon abhielt, sie zu bemerken und zu debuggen. – abarnert

+0

Wenn Sie speziell für Dateien sperren, sollten Sie möglicherweise Advisory-Dateisperren für POSIX und exklusiven Dateizugriff unter Windows anstelle von generischen Thread-/Prozesssperren in Erwägung ziehen. – abarnert

+1

Eine andere Möglichkeit besteht darin, die gesamte Datei aus einem einzelnen Thread (in einem einzigen Prozess) anzuhängen und alle anderen nur Nachrichten in eine Warteschlange zu stellen (die keine Synchronisation benötigt, weil sie eingebaut ist). – abarnert

Antwort

7

Während dieses isn‘ t ganz klar aus the docs, synchronisieren Multiprocessing-Synchronisation Primitiven tatsächlich Threads auch.

Zum Beispiel, wenn Sie diesen Code ausführen:

import multiprocessing 
import sys 
import threading 
import time 

lock = multiprocessing.Lock() 

def f(i): 
    with lock: 
     for _ in range(10): 
      sys.stderr.write(i) 
      time.sleep(1) 

t1 = threading.Thread(target=f, args=['1']) 
t2 = threading.Thread(target=f, args=['2']) 
t1.start() 
t2.start() 
t1.join() 
t2.join() 

... der Ausgang wird immer 1111111111222222222 oder 22222222221111111111, nicht eine Mischung aus beiden.

Die Sperren werden auf Win32-Kernel-Sync-Objekten unter Windows, Semaphoren auf POSIX-Plattformen, die sie unterstützen, und auf anderen Plattformen überhaupt nicht implementiert. (Sie können dies testen, mit import multiprocessing.semaphore, die eine ImportError auf anderen Plattformen erhöhen wird, wie in der Dokumentation erklärt.)


Davon abgesehen, ist es sicherlich sicher zwei Ebenen von Schlössern haben, solange Sie verwenden sie immer in der richtigen Reihenfolge - nehmen Sie nie die threading.Lock, es sei denn, Sie können garantieren, dass Ihr Prozess die multiprocessing.Lock hat.

Wenn Sie dies clever genug tun, kann es Leistungsvorteile haben. (Prozessübergreifende Sperren auf Windows und auf einigen POSIX-Plattformen können um Größenordnungen langsamer sein als Intraprozess-Sperren.)

Wenn Sie es nur in der offensichtlichen Weise tun (nur with threadlock: innerhalb with processlock: Blöcke), es offensichtlich wird Leistung nicht helfen, und in der Tat wird die Dinge ein wenig verlangsamen (obwohl möglicherweise nicht genug zu messen), und es wird keine direkten Vorteile hinzufügen. Natürlich wissen Ihre Leser, dass Ihr Code korrekt ist, auch wenn sie nicht wissen, dass multiprocessing Sperren zwischen Threads funktionieren, und in manchen Fällen kann das Debugging von Prozess-Deadlocks viel einfacher sein als das Debugging von Interprozess-Deadlocks ... aber ich denke nicht das ist ein Grund genug für die zusätzliche Komplexität in den meisten Fällen.

Verwandte Themen