2016-04-25 26 views
0

[Ich habe eine extrem schwierige Zeit, eine thread-/process-safe-Lösung zu implementieren, um eine Dateisperre mit Python 3 unter Linux zu erhalten (ist mir egal über portable Lösungen, da das Programm, an dem ich arbeite, die Linux-Kernel-exklusiven Container-Technologien nutzt.]Auf der Suche nach zuverlässigen Python-Prozess-Synchronisationstechniken (Linux nicht portierbar)

Nachdem ich http://apenwarr.ca/log/?m=201012#13 gelesen hatte, entschied ich mich, fcntl.lockf() zu verwenden, um eine Datei für den prozessexklusiven Zugriff zu sperren und schrieb die folgende Funktion:

import contextlib as Contextlib 
import errno as Errno 
import fcntl as Fcntl 
import os as Os 


@Contextlib.contextmanager 
def exclusiveOpen(filename, 
        mode): 
    try: 
    fileDescriptor = Os.open(filename, 
          Os.O_WRONLY | Os.O_CREAT) 
    except OSError as e: 
    if not e.errno == Errno.EEXIST: 
     raise 

    try: 
    Fcntl.lockf(fileDescriptor, 
       Fcntl.LOCK_EX) 
    fileObject = Os.fdopen(fileDescriptor, 
          mode) 

    try: 
     yield fileObject 
    finally: 
     fileObject.flush() 
     Os.fdatasync(fileDescriptor) 
    finally: 
    Os.close(fileDescriptor) 

von Apart, dass ich bin sicher, dass es in richtig (warum blockiert es nicht in Fcntl.lockf(fileDescriptor, Fcntl.LOCK_EX)?), der Teil, der mich am meisten unbehaglich fühlt, ist, wo die fileDescriptor erworben wird - wenn die Datei nicht vorhanden ist, wird es erstellt ... aber was ist los, wenn zwei Prozesse führen diesen Teil gleichzeitig aus? Gibt es keine Chance auf eine Race Condition, bei der beide Threads versuchen, die Datei zu erstellen? Und wenn ja, wie könnte man das vielleicht verhindern - schon gar nicht mit einer anderen Sperrdatei (?), Weil es auf die gleiche Weise erstellt werden müsste (?!?!) Ich bin verloren. Jede Hilfe wird sehr geschätzt.

UPDATE: Posted eine andere Herangehensweise an das zugrunde liegende Problem. Das Problem, das ich in diesem Ansatz sehe, ist, dass ein Prozedurname nicht mit dem Namen eines vorhandenen UNIX-Domänen-Sockets übereinstimmen muss (möglicherweise von einem anderen Programm erstellt) - stimme ich damit überein?

Antwort

1

AFAIK, beim Erstellen einer Datei ist keine Race Condition möglich. Wenn zwei (oder mehr) Prozesse oder Threads versuchen, die gleiche Datei gleichzeitig mit open mit dem O_CREAT Flag zu erstellen und nicht die O_EXCL, wird die Datei einmal erstellt, und alle Aufrufer erhalten einen geöffneten Dateideskriptor in derselben Datei - genau was du brauchst. Ich nehme an, dass Sie C-Python unter Linux verwenden und dass Ihr Python-Code mit der zugrundeliegenden C open-Funktion endet.

Wenn Ihr Code die lockf-Funktion ausführt, haben Sie einen geöffneten Dateideskriptor für eine vorhandene Datei, sodass der zugrunde liegende lockf-Aufruf garantiert, dass nur ein Prozess die Sperre gleichzeitig halten kann, sofern das zugrunde liegende Dateisystem das Sperren unterstützt.

Ich versuchte es auf einem Unix (FreeBSD) System und es funktioniert wie erwartet.

+0

lockf Block auf Ihrem FreeBSD? – MCH

+0

@MCH Nun konnte ich keine Race Condition reproduzieren, aber ich habe gerade 2 Prozesse gestartet, öffne die gleiche Datei in beiden (aber mit 'O_RDWR' anstelle von' O_RDONLY') und nimm die Sperre zuerst. Wenn ich versuche, die Sperre in der zweiten zu übernehmen, wird sie blockiert, bis der erste Prozess die Sperre aufhebt oder die Datei schließt. Und sobald es das Schloss erworben hat, blockiert es jeden anderen Prozess, der versuchen würde, es zu nehmen. –

-1

Basierend auf https://stackoverflow.com/a/7758075/5449837, endete ich mit abstrakten UNIX-Sockets statt Lock-Dateien, um Prozesse zu synchronisieren, (wenn jemand eine bessere Antwort postet, werde ich es gerne akzeptieren).

Um die Wähler unten: Um zu erklären, warum Sie denken, das ist schlecht?

+0

nicht verwandt: 1- Bitte, nicht umbenennen stdlib-Module. Auch [folgen Sie den Namenskonventionen von pep-8 selbst für Ihre eigenen Module (im öffentlichen Code)] (https://www.python.org/dev/peps/pep-0008/#package-and-module-names) 2 - Stellen Sie keine Fragen in die Antwort (es ist kein Diskussionsforum). Wenn Sie eine Frage haben; [frage es stattdessen als eine Frage] (http://stackoverflow.com/questions/ask) – jfs

+0

Danke (wird das beheben) :) - Aus Neugier, warum ist keine 1? – MCH

+0

warum? Weil Lesbarkeit zählt. Wenn ich 'Socket' sehe; Es sieht aus wie eine benutzerdefinierte Klasse. Wenn ich 'Socket' sehe, nehme ich an, dass es ein stdlib-Modul ist. Keine Notwendigkeit, Reibung ohne Notwendigkeit zu erzeugen – jfs