2009-09-18 62 views
21

我正在写一个Python脚本,可能会或可能不会(取决于一堆事情)运行了很长时间,我想确保多个实例(通过cron启动)不会踩在每个其他脚趾。这样做的逻辑方法似乎是一个基于PID的锁文件...但是我不想重新发明轮子,如果已经有代码来执行此操作。Python:用于创建基于PID的锁文件的模块?

那么,有没有一个Python模块可以管理基于PID的锁文件的细节?

+1

我的答案在这里也许是有趣的:[它使用套接字来创建一个lo即变为即使处理被发送一个SIGKILL远CK文件 - ] [1] [1]:http://stackoverflow.com/questions/788411/check-to-see-if-python-脚本是运行/ 7758075#7758075 – aychedee 2011-11-09 11:15:30

回答

8

如果你可以使用了GPLv2,水银有一个模块:

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

感谢所有其他有用的答案,但这原来是最简单的解决方案,因为添加的mercurial依赖对我来说不是问题(我只是用它“ “实用程序脚本)。 – 2009-10-18 16:54:51

+0

请注意,这个答案不适用于更新版本的mercurial库(编写本文时为3.0.1); 'lock'类在init中需要'vfs'和'file'参数('timeout'是可选的)。 – ropable 2014-06-16 07:16:50

+0

'vfs'参数可以如下生成:'from mercurial import scmutil; vfs = scmutil.vfs(“/”)'。但是,依靠较大产品的内部模块可能不是一个好主意。 – 2015-02-16 11:54:16

4

我相信你会找到必要的信息here。所讨论的页面是指用于在python中构建守护进程的包:此过程涉及创建PID锁文件。

+0

这个模块似乎是Python标准库lockfile模块的一个包装器,它看起来对我来说是原子的。 – 2010-11-13 06:06:05

+0

非常感谢,绝对我期待。 – pylover 2012-04-17 19:36:10

+0

它是通过这个https://github.com/khertan/Khweeteur/blob/master/khweeteur/pydaemon/pidlockfile.py作为Ben Finney的更新代码而被分散修补在github上的。 – 2013-05-29 15:07:38

2

有一个recipe on ActiveState on creating lockfiles

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

+1

ActiveState解决方案对我而言并不重要。我认为它需要用临时名称创建锁定文件,如“lockfile。$ PID”,将PID写入它,然后将“lockfile。$ PID”重命名为“lockfile”。然后通过重新读取lockfile来查看它是否有你的PID。这可能是为了许多目的而矫枉过正,但它是最稳健的方式。 – 2010-11-13 05:59:39

1

我已经与所有那些漂亮的不满,所以我写了这一点:

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

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