Pregunta

Estoy escribiendo un script de Python que puede o no (dependiendo de un montón de cosas) ejecutarse durante mucho tiempo, y me gustaría asegurarme de que varias instancias (comenzadas a través de cron) no interfieran los demás dedos de los pies. La forma lógica de hacer esto parece ser un archivo de bloqueo basado en PID ... Pero no quiero reinventar la rueda si ya hay código para hacerlo.

Entonces, ¿existe un módulo Python que gestione los detalles de un archivo de bloqueo basado en PID?

¿Fue útil?

Solución

Si puede usar GPLv2, Mercurial tiene un módulo para eso:

http://bitbucket.org/mirror/mercurial/src/ tip / mercurial / lock.py

Ejemplo de uso:

from mercurial import error, lock

try:
    l = lock.lock("/path/to/lock", timeout=600) # wait at most 10 minutes
    # do something
except error.LockHeld:
     # couldn't take the lock
else:
    l.release()

Otros consejos

Esto podría serle útil: lockfile

Creo que encontrará la información necesaria aquí . La página en cuestión se refiere a un paquete para construir demonios en python: este proceso implica crear un archivo de bloqueo PID.

He estado bastante descontento con todo eso, así que escribí esto:

class Pidfile():
    def __init__(self, path, log=sys.stdout.write, warn=sys.stderr.write):
        self.pidfile = path
        self.log = log
        self.warn = warn

    def __enter__(self):
        try:
            self.pidfd = os.open(self.pidfile, os.O_CREAT|os.O_WRONLY|os.O_EXCL)
            self.log('locked pidfile %s' % self.pidfile)
        except OSError as e:
            if e.errno == errno.EEXIST:
                pid = self._check()
                if pid:
                    self.pidfd = None
                    raise ProcessRunningException('process already running in %s as pid %s' % (self.pidfile, pid));
                else:
                    os.remove(self.pidfile)
                    self.warn('removed staled lockfile %s' % (self.pidfile))
                    self.pidfd = os.open(self.pidfile, os.O_CREAT|os.O_WRONLY|os.O_EXCL)
            else:
                raise

        os.write(self.pidfd, str(os.getpid()))
        os.close(self.pidfd)
        return self

    def __exit__(self, t, e, tb):
        # return false to raise, true to pass
        if t is None:
            # normal condition, no exception
            self._remove()
            return True
        elif t is PidfileProcessRunningException:
            # do not remove the other process lockfile
            return False
        else:
            # other exception
            if self.pidfd:
                # this was our lockfile, removing
                self._remove()
            return False

    def _remove(self):
        self.log('removed pidfile %s' % self.pidfile)
        os.remove(self.pidfile)

    def _check(self):
        """check if a process is still running

the process id is expected to be in pidfile, which should exist.

if it is still running, returns the pid, if not, return False."""
        with open(self.pidfile, 'r') as f:
            try:
                pidstr = f.read()
                pid = int(pidstr)
            except ValueError:
                # not an integer
                self.log("not an integer: %s" % pidstr)
                return False
            try:
                os.kill(pid, 0)
            except OSError:
                self.log("can't deliver signal to %s" % pid)
                return False
            else:
                return pid

class ProcessRunningException(BaseException):
    pass

para usar algo como esto:

try:
    with Pidfile(args.pidfile):
        process(args)
except ProcessRunningException:
    print "the pid file is in use, oops."

Sé que este es un hilo antiguo, pero también creé un bloqueo simple que solo se basa en bibliotecas nativas de Python:

import fcntl
import errno


class FileLock:
    def __init__(self, filename=None):
        self.filename = os.path.expanduser('~') + '/LOCK_FILE' if filename is None else filename
        self.lock_file = open(self.filename, 'w+')

    def unlock(self):
        fcntl.flock(self.lock_file, fcntl.LOCK_UN)

    def lock(self, maximum_wait=300):
        waited = 0
        while True:
            try:
                fcntl.flock(self.lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
                return True
            except IOError as e:
                if e.errno != errno.EAGAIN:
                    raise e
                else:
                    time.sleep(1)
                    waited += 1
                    if waited >= maximum_wait:
                        return False

Hay una receta en ActiveState al crear archivos de bloqueo .

Para generar el nombre de archivo puede utilizar os.getpid () para obtener el PID.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top