Python: module for creating PID-based lockfile?
我正在编写一个python脚本,它可能运行很长时间,也可能不运行(取决于一堆东西),我想确保多个实例(通过cron启动)不会相互影响。这样做的逻辑方法似乎是一个基于PID的锁文件……但我不想重新发明轮子,如果已经有代码可以这样做的话。
那么,是否有一个python模块来管理基于pid的锁文件的细节?
这可能对您有所帮助:lockfile
如果您可以使用gplv2,那么Mercurial有一个模块:
http://bitback.org/mirror/mercurial/src/tip/mercurial/lock.py
示例用法:
1 2 3 4 5 6 7 8 9 | 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() |
我相信你会在这里找到必要的信息。有问题的页面引用了一个包,用于在python中构建守护进程:这个过程涉及到创建一个pid锁文件。
我知道这是一个旧线程,但我还创建了一个简单的锁,它只依赖于Python本机库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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 |
我对这些都很不满意,所以我写了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | 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 |
像这样使用:
1 2 3 4 5 | try: with Pidfile(args.pidfile): process(args) except ProcessRunningException: print"the pid file is in use, oops." |
在ActiveState上有一个创建锁文件的方法。
要生成文件名,可以使用os.get pid()获取pid。