Python:用于创建基于 PID 的锁文件的模块?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1444790/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 22:15:23  来源:igfitidea点击:

Python: module for creating PID-based lockfile?

pythonpidlockfile

提问by David Wolever

I'm writing a Python script that may or may not (depending on a bunch of things) run for a long time, and I'd like to make sure that multiple instances (started via cron) don't step on each others toes. The logical way to do this seems to be a PID-based lockfile… But I don't want to re-invent the wheel if there is already code to do this.

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

So, is there a Python module out there which will manage the details of a PID-based lockfile?

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

采纳答案by tonfa

If you can use GPLv2, Mercurial has a module for that:

如果您可以使用 GPLv2,Mercurial 有一个模块:

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

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

Example usage:

用法示例:

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()

回答by ennuikiller

This might be of help to you: lockfile

这可能对您有所帮助: lockfile

回答by jldupont

I believe you will find the necessary information here. The page in question refers to a package for building daemons in python: this process involves creating a PID lockfile.

我相信您会在这里找到必要的信息。有问题的页面指的是在 python 中构建守护进程的包:这个过程涉及创建一个 PID 锁文件。

回答by anarcat

i've been pretty unhappy with all of those, so i wrote this:

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

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

to be used something like this:

要使用这样的东西:

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

回答by shahins

I know this is an old thread, but I also created a simple lock which only relies on python native libraries:

我知道这是一个旧线程,但我也创建了一个简单的锁,它只依赖于 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

回答by Andre Miller

There is a recipe on ActiveState on creating lockfiles.

ActiveState 上有一个关于创建锁文件的方法

To generate the filename you can use os.getpid()to get the PID.

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

回答by Hosana Gomes

You can try PID: https://pypi.org/project/pid/

你可以试试PIDhttps: //pypi.org/project/pid/

As the documentation shows, you can lock a function simply adding the decorator @pidfile()on the top of function/method name.

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

from pid.decorator import pidfile


@pidfile()
def main():
  pass

if __name__ == "__main__":
  main()

The default location for pidfile self check (the file who says if you can execute the code or not) is '/var/run'. You can change it as follows:

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

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

For other params, see: https://github.com/trbs/pid/blob/95499b30e8ec4a473c0e6b407c03ce644f61c643/pid/base.py#L41

对于其他参数,请参阅:https: //github.com/trbs/pid/blob/95499b30e8ec4a473c0e6b407c03ce644f61c643/pid/base.py#L41

Unfortunatly, this lib's documentation is a little bit poor.

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