我正在写一个Python脚本,可能会或可能不会(取决于很多东西)运行很长时间,我想确保多个实例(通过cron启动)不会踩彼此的脚趾。这样做的逻辑方法似乎是一个基于PID的锁文件...但是如果已经有代码执行此操作,我不想重新发明轮子。
那么,是否有一个Python模块可以管理基于PID的锁文件的细节?
答案 0 :(得分:12)
这可能对您有所帮助:lockfile
答案 1 :(得分:9)
如果你可以使用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()
答案 2 :(得分:4)
我相信您会找到必要的信息here。有问题的页面是指用于在python中构建守护进程的包:此过程涉及创建PID锁定文件。
答案 3 :(得分: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 :(得分:3)
我知道这是一个旧线程,但我还创建了一个只依赖于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
答案 5 :(得分:2)
有recipe on ActiveState on creating lockfiles。
要生成文件名,您可以使用os.getpid()来获取PID。
答案 6 :(得分:1)
您可以尝试 PID :https://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
不幸的是,这个lib的文档有点差。