28

我正在编写一个 Python 脚本,它可能会或可能不会(取决于一堆东西)运行很长时间,我想确保多个实例(通过 cron 启动)不会互相踩到脚趾. 执行此操作的合乎逻辑的方法似乎是基于 PID 的锁定文件……但如果已经有代码可以执行此操作,我不想重新发明轮子。

那么,是否有一个 Python 模块可以管理基于 PID 的锁定文件的详细信息?

4

7 回答 7

13

这可能对您有帮助: lockfile

于 2009-09-18T14:18:47.223 回答
11

如果你可以使用 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()
于 2009-09-18T14:19:54.727 回答
6

我对所有这些都很不满意,所以我写了这个:

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."
于 2014-05-23T19:23:50.843 回答
5

我知道这是一个旧线程,但我还创建了一个仅依赖于 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
于 2017-03-23T04:31:03.890 回答
4

我相信你会在这里找到必要的信息。有问题的页面是指用于在 python 中构建守护程序的包:此过程涉及创建 PID 锁定文件。

于 2009-09-18T14:20:16.093 回答
2

ActiveState 上有一个关于创建 lockfiles 的配方

要生成文件名,您可以使用os.getpid()来获取 PID。

于 2009-09-18T14:23:15.637 回答
2

您可以尝试PIDhttps ://pypi.org/project/pid/

如文档所示,您只需@pidfile()在函数/方法名称的顶部添加装饰器即可锁定函数。

from pid.decorator import pidfile


@pidfile()
def main():
  pass

if __name__ == "__main__":
  main()

pidfile 自检的默认位置(说明您是否可以执行代码的文件)是“/var/run”。您可以按如下方式更改它:

@pidfile(piddir='/path/to/a/custom/location')

其他参数见:https ://github.com/trbs/pid/blob/95499b30e8ec4a473c0e6b407c03ce644f61c643/pid/base.py#L41

不幸的是,这个库的文档有点差。

于 2020-02-12T19:59:30.343 回答