Versuchen Sie es mit der ilock-Bibliothek:
from ilock import ILock
with ILock('Unique lock name'):
# The code should be run as a system-wide single instance
...
Die "traditionelle" Unix-Antwort besteht darin, Dateisperren zu verwenden. Sie können lockf(3)
verwenden Abschnitte einer Datei zu sperren, damit andere Prozesse sie nicht bearbeiten können; Ein sehr häufiger Missbrauch ist die Verwendung als Mutex zwischen Prozessen. Das Python-Äquivalent ist fcntl.lockf.
Traditionell schreiben Sie die PID des Sperrprozesses in die Sperrdatei, damit Deadlocks aufgrund von Prozessen, die sterben, während die Sperre gehalten wird, identifizierbar und behebbar sind.
Damit erhalten Sie, was Sie wollen, da sich Ihre Sperre in einem globalen Namensraum (dem Dateisystem) befindet und für alle Prozesse zugänglich ist. Dieser Ansatz hat auch den Vorteil, dass Nicht-Python-Programme an Ihrer Sperrung teilnehmen können. Der Nachteil ist, dass Sie einen Platz brauchen, an dem diese Sperrdatei leben kann; Außerdem werden einige Dateisysteme nicht richtig gesperrt, sodass das Risiko besteht, dass der Ausschluss stillschweigend nicht erreicht wird. Du gewinnst einige, du verlierst einige.
Meine Antwort überschneidet sich mit den anderen Antworten, aber nur um etwas hinzuzufügen, das die Leute kopieren und einfügen können, mache ich oft so etwas.
class Locker:
def __enter__ (self):
self.fp = open("./lockfile.lck")
fcntl.flock(self.fp.fileno(), fcntl.LOCK_EX)
def __exit__ (self, _type, value, tb):
fcntl.flock(self.fp.fileno(), fcntl.LOCK_UN)
self.fp.close()
Und dann verwenden Sie es als:
print("waiting for lock")
with Locker():
print("obtained lock")
time.sleep(5.0)
Führen Sie zum Testen touch lockfile.lck
aus Führen Sie dann den obigen Code in zwei oder mehr verschiedenen Terminals aus (aus demselben Verzeichnis).
UPDATE:smwikipedia hat erwähnt, dass meine Lösung unixspezifisch ist. Ich brauchte kürzlich eine portable Version und kam mit der Idee aus einem zufälligen Github-Projekt auf Folgendes. Ich bin mir nicht sicher, ob die seek()-Aufrufe benötigt werden, aber sie sind da, weil die Windows-API eine bestimmte Position in der Datei sperrt. Wenn Sie die Datei für nichts anderes als zum Sperren verwenden, können Sie wahrscheinlich die Suchvorgänge entfernen.
if os.name == "nt":
import msvcrt
def portable_lock(fp):
fp.seek(0)
msvcrt.locking(fp.fileno(), msvcrt.LK_LOCK, 1)
def portable_unlock(fp):
fp.seek(0)
msvcrt.locking(fp.fileno(), msvcrt.LK_UNLCK, 1)
else:
import fcntl
def portable_lock(fp):
fcntl.flock(fp.fileno(), fcntl.LOCK_EX)
def portable_unlock(fp):
fcntl.flock(fp.fileno(), fcntl.LOCK_UN)
class Locker:
def __enter__(self):
self.fp = open("./lockfile.lck")
portable_lock(self.fp)
def __exit__(self, _type, value, tb):
portable_unlock(self.fp)
self.fp.close()
Der POSIX-Standard spezifiziert prozessübergreifende Semaphore, die für diesen Zweck verwendet werden können. http://linux.die.net/man/7/sem_overview
Die multiprocessing
Modul in Python basiert auf dieser API und anderen. Insbesondere multiprocessing.Lock
bietet einen prozessübergreifenden "Mutex". http://docs.python.org/library/multiprocessing.html#synchronization-between-processes
BEARBEITEN um auf die bearbeitete Frage zu antworten:
In Ihrem Proof of Concept erstellt jeder Prozess einen Lock()
. Sie haben also zwei separate Schlösser. Deshalb wartet keiner der Prozesse. Sie müssen die gleiche Sperre zwischen Prozessen teilen. Der Abschnitt, auf den ich im multiprocessing
verlinkt habe Dokumentation erklärt, wie das geht.