Python: модуль для создания файла блокировки на основе PID?
Я пишу Python script, который может или не может (в зависимости от множества вещей) работать в течение длительного времени, и я хотел бы убедиться, что несколько экземпляров (запущенных через cron) не шаг друг на друга. Логичный способ сделать это, похоже, является файлом блокировки на основе PID... Но я не хочу заново изобретать колесо, если для этого уже есть код.
Итак, есть ли там модуль Python, который будет управлять деталями файла блокировки на основе PID?
Ответы
Ответ 1
Если вы можете использовать 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
Это может вам помочь: lockfile
Ответ 3
Я считаю, что вы найдете необходимую информацию здесь. Эта страница относится к пакету для создания демонов в python: этот процесс включает создание файла блокировки PID.
Ответ 4
Существует рецепт в ActiveState при создании lockfiles.
Чтобы сгенерировать имя файла, вы можете использовать os.getpid() для получения PID.
Ответ 5
Я был очень недоволен всеми этими, поэтому я написал это:
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."
Ответ 6
Я знаю, что это старый поток, но я также создал простую блокировку, которая использует только собственные библиотеки 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