2009-09-18 2 views
21

Я пишу скрипт Python, который может или не может (в зависимости от множества вещей) работать в течение длительного времени, и я хотел бы убедиться, что несколько экземпляров (запущенных через cron) не наступают на каждый другие пальцы ног. Логичным способом сделать это кажется файлом блокировки на основе PID ... Но я не хочу заново изобретать колесо, если для этого уже есть код.Python: модуль для создания файла блокировки на основе PID?

Итак, есть ли модуль Python, который будет управлять деталями файла блокировки на основе PID?

+1

Мой ответ здесь также может быть интересен: [Он использует сокеты для создания lo ск файл, который уходит, даже если процесс посылается SIGKILL -] [1] [1]: http://stackoverflow.com/questions/788411/check-to-see-if-python- script-is-running/7758075 # 7758075 – aychedee

ответ

8

Если вы можете использовать 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() 
+0

Спасибо за все остальные полезные ответы, но это оказалось самым простым решением, так как добавленная зависимость от меркуриума для меня не проблема (я просто использую ее для «мало «служебные скрипты». –

+0

Обратите внимание, что этот ответ не работает с более новыми версиями библиотеки меркуриалов (3.0.1 на момент написания); класс 'lock' ожидает как' vfs', так и 'file' args в init (' timeout' является необязательным). – ropable

+0

Аргумент 'vfs' может быть сгенерирован следующим образом:' from mercurial import scmutil; vfs = scmutil.vfs ("/") '. Однако, полагаясь на внутренний модуль более крупного продукта, вероятно, не такая хорошая идея. –

4

I верьте, что вы найдете необходимую информацию here. Эта страница относится к пакету для создания демонов в python: этот процесс включает создание файла блокировки PID.

+0

Этот модуль, кажется, является оберткой поверх стандартного модуля блокировки библиотеки Python, который выглядит как атомный для меня. –

+0

Большое спасибо, абсолютно ожидаю. – pylover

+0

Это разбросанный по github файл с https://github.com/khertan/Khweeteur/blob/master/khweeteur/pydaemon/pidlockfile.py, являющимся более поздним кодом от Ben Finney. –

2

Существует recipe on ActiveState on creating lockfiles.

Чтобы сгенерировать имя файла, вы можете использовать os.getpid(), чтобы получить PID.

+1

Решение ActiveState не выглядит атомным для меня. Я думаю, что ему нужно создать файл блокировки с временным именем, например «lockfile. $ PID», записать в него PID, а затем переименовать «lockfile. $ PID» в «lockfile». Затем проверьте, перечитав файл блокировки, чтобы узнать, есть ли у него PID. Это, вероятно, слишком много для многих целей, но это самый надежный способ. –

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 
Смежные вопросы