Python: модуль для создания PID-файла блокировки? - PullRequest
23 голосов
/ 18 сентября 2009

Я пишу скрипт на Python, который может или не может (в зависимости от множества вещей) работать долго, и я хотел бы убедиться, что несколько экземпляров (запущенных через cron) не наступают пальцы друг друга. Логичным способом сделать это, по-видимому, является файл блокировки на основе PID… Но я не хочу заново изобретать колесо, если для этого уже есть код.

Итак, есть ли модуль Python, который будет управлять деталями файла блокировки на основе PID?

Ответы [ 6 ]

12 голосов
/ 18 сентября 2009

Это может вам помочь: lockfile

9 голосов
/ 18 сентября 2009

Если вы можете использовать GPLv2, у Mercurial есть модуль для этого:

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

Пример использования:

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()
4 голосов
/ 18 сентября 2009

Я полагаю, вы найдете необходимую информацию здесь . Данная страница относится к пакету для создания демонов в python: этот процесс включает создание файла блокировки PID.

3 голосов
/ 23 марта 2017

Я знаю, что это старый поток, но я также создал простую блокировку, которая опирается только на собственные библиотеки 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
3 голосов
/ 23 мая 2014

Я был очень недоволен всем этим, поэтому я написал это:

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

будет использоваться примерно так:

try:
    with Pidfile(args.pidfile):
        process(args)
except ProcessRunningException:
    print "the pid file is in use, oops."
2 голосов
/ 18 сентября 2009

В ActiveState есть рецепт для создания файлов блокировки .

Для генерации имени файла вы можете использовать os.getpid () , чтобы получить PID.

...