Threadsafe und fehlertolerante Dateischreibvorgänge

8

Ich habe einen langwierigen Prozess, der viele Dinge in eine Datei schreibt. Das Ergebnis sollte alles oder nichts sein, also schreibe ich in eine temporäre Datei und benenne sie am Ende in den richtigen Namen um. Momentan ist mein Code wie folgt:

%Vor%

Ich bin aus mehreren Gründen nicht glücklich damit:

  • Es wird nicht sauber bereinigt, wenn eine Ausnahme auftritt
  • ignoriert Concurrency-Probleme
  • es ist nicht wiederverwendbar (ich brauche das an verschiedenen Stellen in meinem Programm)

Irgendwelche Vorschläge, wie ich meinen Code verbessern kann? Gibt es eine Bibliothek, die mir helfen kann?

    
georg 17.08.2012, 10:09
quelle

4 Antworten

11

Sie können Pythons tempfile Modul verwenden, um Ihnen einen temporären Dateinamen zu geben. Es kann eine temporäre Datei in einer Thread-sicheren Weise erstellen, anstatt eine mit time.time() zu erstellen, die denselben Namen zurückgeben kann, wenn sie in mehreren Threads gleichzeitig verwendet wird.

Wie in einem Kommentar zu Ihrer Frage vorgeschlagen, kann dies mit der Verwendung eines Kontextmanagers gekoppelt werden. Sie können einige Ideen zur Implementierung von dem, was Sie tun möchten, indem Sie sich Python tempfile.py Quellen ansehen.

Das folgende Code-Snippet kann tun, was Sie wollen. Es verwendet einige der Interna der Objekte, die von tempfile zurückgegeben werden.

  • Die Erstellung von temporären Dateien ist threadsicher.
  • Das Umbenennen von Dateien nach erfolgreichem Abschluss ist atomisch, zumindest unter Linux. Es gibt keinen separaten Check zwischen os.path.exists() und os.rename() , der eine Race-Bedingung verursachen könnte. Für eine atomare Umbenennung unter Linux müssen die Quelle und die Ziele auf demselben Dateisystem liegen, weshalb dieser Code die temporäre Datei im selben Verzeichnis wie die Zieldatei ablegt.
  • Die RenamedTemporaryFile -Klasse sollte sich für die meisten Zwecke wie ein NamedTemporaryFile verhalten, außer wenn sie mit dem Kontextmanager geschlossen wird, wird die Datei umbenannt.

Beispiel:

%Vor%

Sie können es dann wie folgt verwenden:

%Vor%

Beim Schreiben geht der Inhalt in eine temporäre Datei, beim Beenden wird die Datei umbenannt. Dieser Code wird wahrscheinlich einige Verbesserungen benötigen, aber die allgemeine Idee sollte Ihnen helfen, loszulegen.

    
Austin Phillips 17.08.2012, 14:40
quelle
5

Um alles oder nichts zuverlässig in eine Datei zu schreiben:

%Vor%

Siehe auch Ist rename () ohne fsync () sicher? (wird von @Mihai Stan )

Verwendung

%Vor%

Um fehlende os.replace() zu implementieren, können Sie MoveFileExW(src, dst, MOVEFILE_REPLACE_EXISTING) (über win32file- oder ctypes-Module) unter Windows.

Bei mehreren Threads können Sie queue.put(data) aufrufen verschiedene Threads und schreibe in Datei in einem eigenen Thread:

%Vor%

queue.put(None) unterbricht die Schleife.

Als Alternative könnten Sie Sperren (Threading, Multiprocessing, filelock) um den Zugriff zu synchronisieren:

%Vor%     
jfs 17.08.2012 20:34
quelle
2

Sie können das Sperrdateimodul verwenden, um die Datei zu sperren, während Sie darauf schreiben. Jeder nachfolgende Versuch, es zu sperren, wird blockiert, bis die Sperre des vorherigen Prozesses / Threads freigegeben wurde.

%Vor%

Auf diese Weise umgehen Sie Ihre Nebenläufigkeitsprobleme und müssen keine übrig gebliebene Datei bereinigen, wenn eine Ausnahme auftritt.

    
mstud 17.08.2012 10:31
quelle
2

Das Konstrukt with ist nützlich für die Bereinigung beim Beenden, aber nicht für das gewünschte Commit / Rollback-System. Ein try / except / else -Block kann dafür verwendet werden.

Sie sollten auch eine Standardmethode zum Erstellen des temporären Dateinamens verwenden, zum Beispiel mit dem Modul tempfile .

Und denken Sie daran fsync vor dem Umbenennen

Nachfolgend finden Sie den vollständigen modifizierten Code:

%Vor%     
Mihai Stan 17.08.2012 14:47
quelle

Tags und Links