【问题标题】:Python: module for creating PID-based lockfile?Python:用于创建基于 PID 的锁定文件的模块?
【发布时间】:2010-11-29 12:13:57
【问题描述】:

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

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

【问题讨论】:

标签: python pid lockfile


【解决方案1】:

这可能对你有帮助:lockfile

【讨论】:

    【解决方案2】:

    如果你可以使用 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()
    

    【讨论】:

    • 感谢所有其他有用的答案,但事实证明这是最简单的解决方案,因为添加的 mercurial 依赖对我来说不是问题(我只是将它用于“小”实用程序脚本)。
    • 请注意,此答案不适用于较新版本的 mercurial 库(撰写本文时为 3.0.1); lock 类在初始化时需要 vfsfile 参数(timeout 是可选的)。
    • vfs 参数可以生成如下:from mercurial import scmutil; vfs = scmutil.vfs("/")。但是,依靠更大产品的内部模块可能不是一个好主意。
    • 锁定功能出现以下错误:TypeError: __init__() missing 1 required positional argument: 'fname'。我还想要一个名为 vfs 的变量
    【解决方案3】:

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

    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."
    

    【讨论】:

      【解决方案4】:

      我知道这是一个旧线程,但我还创建了一个仅依赖于 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
      

      【讨论】:

      • 您好,我对 python 很陌生,我收到了一个项目,因为它已经两年没有维护了,所以所有依赖项都是错误的。拥有锁定文件可以防止这种情况发生。我的问题是我需要把这个文件放在哪里?
      【解决方案5】:

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

      【讨论】:

      【解决方案6】:

      有一个recipe on ActiveState on creating lockfiles

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

      【讨论】:

      • ActiveState 解决方案在我看来并不是原子的。我认为它需要使用临时名称创建锁定文件,例如“lockfile.$PID”,将 PID 写入其中,然后将“lockfile.$PID”重命名为“lockfile”。然后通过重新读取锁定文件来检查它是否具有您的 PID。对于许多目的来说,这可能有点矫枉过正,但它是最可靠的方法。
      【解决方案7】:

      你可以试试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

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

      【讨论】:

        猜你喜欢
        • 2013-12-27
        • 1970-01-01
        • 2018-03-27
        • 2022-08-03
        • 1970-01-01
        • 2018-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多