Pruebe la biblioteca ilock:
from ilock import ILock
with ILock('Unique lock name'):
# The code should be run as a system-wide single instance
...
La respuesta "tradicional" de Unix es usar bloqueos de archivos. Puedes usar lockf(3)
para bloquear secciones de un archivo para que otros procesos no puedan editarlo; un abuso muy común es usar esto como un mutex entre procesos. El equivalente de Python es fcntl.lockf.
Tradicionalmente, escribe el PID del proceso de bloqueo en el archivo de bloqueo, de modo que los puntos muertos debidos a que los procesos mueren mientras se mantiene el bloqueo son identificables y reparables.
Esto le da lo que quiere, ya que su bloqueo está en un espacio de nombres global (el sistema de archivos) y accesible para todos los procesos. Este enfoque también tiene la ventaja de que los programas que no son de Python pueden participar en su bloqueo. La desventaja es que necesita un lugar para que viva este archivo de bloqueo; Además, algunos sistemas de archivos en realidad no se bloquean correctamente, por lo que existe el riesgo de que falle silenciosamente en lograr la exclusión. Ganas algo, pierdes algo.
Mi respuesta se superpone con las otras respuestas, pero solo para agregar algo que la gente pueda copiar y pegar, a menudo hago algo como esto.
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()
Y luego úsalo como:
print("waiting for lock")
with Locker():
print("obtained lock")
time.sleep(5.0)
Para probar, haz touch lockfile.lck
luego ejecute el código anterior en dos o más terminales diferentes (desde el mismo directorio).
ACTUALIZACIÓN:smwikipedia mencionó que mi solución es específica de Unix. Necesitaba una versión portátil recientemente y se me ocurrió lo siguiente, con la idea de un proyecto aleatorio de github. No estoy seguro de que se necesiten las llamadas a seek(), pero están ahí porque la API de Windows bloquea una posición específica en el archivo. Si no está utilizando el archivo para otra cosa que no sea el bloqueo, probablemente pueda eliminar las búsquedas.
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()
El estándar POSIX especifica semáforos entre procesos que pueden usarse para este propósito. http://linux.die.net/man/7/sem_overview
El multiprocessing
El módulo en Python se basa en esta API y otras. En particular, multiprocessing.Lock
proporciona un "mutex" entre procesos. http://docs.python.org/library/multiprocessing.html#synchronization- between-processes
EDITAR para responder a la pregunta editada:
En su prueba de concepto, cada proceso está construyendo un Lock()
. Entonces tienes dos cerraduras separadas. Es por eso que ningún proceso espera. Deberá compartir el mismo bloqueo entre procesos. La sección a la que me vinculé en el multiprocessing
la documentación explica cómo hacerlo.