Prova la libreria ilock:
from ilock import ILock
with ILock('Unique lock name'):
# The code should be run as a system-wide single instance
...
La risposta Unix "tradizionale" è usare i blocchi dei file. Puoi usare lockf(3)
bloccare sezioni di un file in modo che altri processi non possano modificarlo; un abuso molto comune è usarlo come mutex tra i processi. L'equivalente di Python è fcntl.lockf.
Tradizionalmente si scrive il PID del processo di blocco nel file di blocco, in modo che i deadlock dovuti a processi che muoiono mentre si mantiene il blocco siano identificabili e risolvibili.
Questo ti dà quello che vuoi, dal momento che il tuo blocco è in uno spazio dei nomi globale (il filesystem) e accessibile a tutti i processi. Questo approccio ha anche il vantaggio che i programmi non Python possono partecipare al tuo blocco. Lo svantaggio è che hai bisogno di un posto dove vivere questo file di blocco; inoltre, alcuni filesystem in realtà non si bloccano correttamente, quindi c'è il rischio che non riesca silenziosamente a ottenere l'esclusione. Ne vinci un po', ne perdi un po'.
La mia risposta si sovrappone alle altre risposte, ma solo per aggiungere qualcosa che le persone possono copiare e incollare, spesso faccio qualcosa del genere.
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()
E poi usalo come:
print("waiting for lock")
with Locker():
print("obtained lock")
time.sleep(5.0)
Per testare, esegui touch lockfile.lck
quindi eseguire il codice precedente in due o più terminali diversi (dalla stessa directory).
AGGIORNAMENTO:smwikipedia ha menzionato che la mia soluzione è specifica per Unix. Di recente avevo bisogno di una versione portatile e mi è venuta in mente quanto segue, con l'idea di un progetto github casuale. Non sono sicuro che le chiamate seek() siano necessarie ma sono presenti perché l'API di Windows blocca una posizione specifica nel file. Se non stai usando il file per qualcosa di diverso dal blocco, probabilmente puoi rimuovere le ricerche.
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()
Lo standard POSIX specifica i semafori tra processi che possono essere utilizzati per questo scopo. http://linux.die.net/man/7/sem_overview
Il multiprocessing
Il modulo in Python è costruito su questa API e altri. In particolare, multiprocessing.Lock
fornisce un "mutex" cross-process. http://docs.python.org/library/multiprocessing.html#synchronization-between-processes
MODIFICA per rispondere alla domanda modificata:
Nella tua prova di concetto ogni processo sta costruendo un Lock()
. Quindi hai due serrature separate. Ecco perché nessuno dei due processi attende. Dovrai condividere lo stesso blocco tra i processi. La sezione a cui mi sono collegato nel multiprocessing
la documentazione spiega come farlo.