Ich muss eine Datei zum Schreiben in Python sperren. Auf die Datei wird von mehreren Python-Prozessen gleichzeitig zugegriffen. Ich habe im Internet einige Lösungen gefunden, aber die meisten sind für meine Zwecke ungeeignet, da sie oft nur auf Unix oder Windows basieren.
Antworten
Zu viele Anzeigen?Das Sperren ist plattform- und gerätespezifisch, aber im Allgemeinen haben Sie einige Möglichkeiten:
- Verwenden Sie flock() oder ein gleichwertiges Programm (wenn Ihr Betriebssystem es unterstützt). Dies ist eine beratende Sperre, die ignoriert wird, wenn Sie nicht auf die Sperre prüfen.
- Verwenden Sie eine lock-copy-move-unlock-Methode, bei der Sie die Datei kopieren, die neuen Daten schreiben und sie dann verschieben (verschieben, nicht kopieren - verschieben ist unter Linux eine atomare Operation - überprüfen Sie Ihr Betriebssystem) und prüfen Sie, ob die Sperrdatei vorhanden ist.
- Verwenden Sie ein Verzeichnis als "Schloss". Dies ist notwendig, wenn Sie auf NFS schreiben, da NFS flock() nicht unterstützt.
- Es gibt auch die Möglichkeit, gemeinsamen Speicher zwischen den Prozessen zu verwenden, aber das habe ich nie ausprobiert; es ist sehr betriebssystemspezifisch.
Bei all diesen Methoden müssen Sie eine Spin-Lock-Technik (Wiederholung nach Fehlschlag) verwenden, um die Sperre zu erhalten und zu testen. Dies lässt ein kleines Fenster für Fehlsynchronisation, aber es ist im Allgemeinen klein genug, um kein großes Problem darzustellen.
Wenn Sie nach einer plattformübergreifenden Lösung suchen, sollten Sie sich besser über einen anderen Mechanismus bei einem anderen System anmelden (die nächstbeste Möglichkeit ist die oben beschriebene NFS-Technik).
Beachten Sie, dass Sqlite über NFS den gleichen Beschränkungen unterliegt wie normale Dateien. Sie können also nicht in eine Sqlite-Datenbank auf einer Netzwerkfreigabe schreiben und die Synchronisierung kostenlos erhalten.
Hier ist ein Beispiel für die Verwendung der filelock Bibliothek, die ähnlich ist wie die Evan Fossmark's Umsetzung :
from filelock import FileLock
lockfile = r"c:\scr.txt"
lock = FileLock(lockfile + ".lock")
with lock:
file = open(path, "w")
file.write("123")
file.close()
Jeder Code innerhalb der with lock:
Block ist thread-sicher, d.h. er wird beendet, bevor ein anderer Prozess Zugriff auf die Datei hat.
Das Sperren einer Datei ist in der Regel ein plattformspezifischer Vorgang, so dass Sie möglicherweise die Möglichkeit berücksichtigen müssen, dass die Datei auf verschiedenen Betriebssystemen läuft. Zum Beispiel:
import os
def my_lock(f):
if os.name == "posix":
# Unix or OS X specific locking here
elif os.name == "nt":
# Windows specific locking here
else:
print "Unknown operating system, lock unavailable"
Ich habe an einer Situation wie dieser gearbeitet, in der ich mehrere Kopien desselben Programms im selben Verzeichnis/Ordner ausführe und Fehler protokolliere. Mein Ansatz war, eine "Sperrdatei" auf die Festplatte zu schreiben, bevor ich die Protokolldatei öffne. Das Programm prüft, ob die Sperrdatei vorhanden ist, bevor es fortfährt, und wartet, bis es an der Reihe ist, wenn die Sperrdatei vorhanden ist.
Hier ist der Code:
def errlogger(error):
while True:
if not exists('errloglock'):
lock = open('errloglock', 'w')
if exists('errorlog'): log = open('errorlog', 'a')
else: log = open('errorlog', 'w')
log.write(str(datetime.utcnow())[0:-7] + ' ' + error + '\n')
log.close()
remove('errloglock')
return
else:
check = stat('errloglock')
if time() - check.st_ctime > 0.01: remove('errloglock')
print('waiting my turn')
EDIT--- Nachdem ich über einige der obigen Kommentare zu veralteten Sperren nachgedacht habe, habe ich den Code geändert, um eine Prüfung auf Veralterung der "Sperrdatei" hinzuzufügen. Die Zeitmessung von mehreren tausend Iterationen dieser Funktion auf meinem System ergab einen Durchschnitt von 0,002066... Sekunden von gerade zuvor:
lock = open('errloglock', 'w')
bis kurz danach:
remove('errloglock')
Also dachte ich mir, ich beginne mit dem Fünffachen dieser Menge, um den Stillstand anzuzeigen und die Situation auf Probleme zu überwachen.
Außerdem habe ich bei der Arbeit mit dem Timing festgestellt, dass ich ein Stück Code hatte, das nicht wirklich notwendig war:
lock.close()
den ich unmittelbar nach der offenen Aussage hatte, habe ich in dieser Bearbeitung entfernt.