2009-09-18 12 views
21

Piszę skrypt w języku Python, który może, ale nie musi (w zależności od wielu rzeczy) działać przez długi czas, i chciałbym się upewnić, że wiele instancji (uruchomionych przez crona) nie nadepnie na inne palce. Logicznym sposobem na zrobienie tego wydaje się być plik blokujący oparty na PID ... Ale nie chcę ponownie wymyślać koła, jeśli jest już kod, który to zrobi.Python: moduł do tworzenia pliku blokującego opartego na PID?

Czy istnieje moduł Pythona, który będzie zarządzał szczegółami pliku blokującego opartego na PID?

+1

Moja odpowiedź tutaj może być również interesująca: [Używa gniazd do tworzenia lo Plik ck że odchodzi, nawet jeśli proces jest wysłał SIGKILL -] [1] [1]: http://stackoverflow.com/questions/788411/check-to-see-if-python- script-is-running/7758075 # 7758075 – aychedee

Odpowiedz

8

Jeśli można użyć GPLv2, Mercurial ma moduł na to:

http://bitbucket.org/mirror/mercurial/src/tip/mercurial/lock.py

Przykład użycia:

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

Dziękuję za wszystkie inne pomocne odpowiedzi, ale okazało się to najprostszym rozwiązaniem, ponieważ dodana zależność mercurialna nie jest dla mnie problemem (używam go tylko dla "mało" "skrypty narzędziowe). –

+0

Należy zauważyć, że ta odpowiedź nie działa z nowszymi wersjami biblioteki mercurial (3.0.1 w momencie pisania); klasa 'lock' oczekuje zarówno argumentów' vfs' i 'file' na init (' timeout' jest opcjonalne). – ropable

+0

'vfs' argument może być generowany w następujący sposób:' from mercurial import scmutil; vfs = scmutil.vfs ("/") '. Jednak poleganie na wewnętrznym module większego produktu prawdopodobnie nie jest dobrym pomysłem. –

4

I wierzę, że znajdziesz potrzebne informacje here. Strona, o której mowa, odnosi się do pakietu do budowania demonów w Pythonie: proces ten polega na tworzeniu pliku blokującego PID.

+0

Ten moduł wydaje się być wrapperem na modułowym module blokującym biblioteki Python, który wygląda na atomowy. –

+0

wielkie dzięki, absolutnie się tego spodziewam. – pylover

+0

Jest to łatka rozproszona na githubie za pomocą tego https://github.com/khertan/Khweeteur/blob/master/khweeteur/pydaemon/pidlockfile.py będącego nowszym kodem autorstwa Bena Finneya. –

2

Istnieje recipe on ActiveState on creating lockfiles.

Aby wygenerować nazwę pliku, można użyć os.getpid(), aby uzyskać identyfikator PID.

+1

Rozwiązanie ActiveState nie wygląda na atomowe. Myślę, że musi utworzyć plik blokady z tymczasową nazwą, taką jak "plik blokujący. $ PID", wpisać do niego PID, a następnie zmienić nazwę pliku "lockfile. $ PID" na "plik blokujący". Następnie sprawdź, ponownie czytając plik blokujący, aby sprawdzić, czy ma on swój PID. Prawdopodobnie jest to przesada w wielu celach, ale jest to najbardziej niezawodny sposób. –

1

byłem bardzo zadowolony z tych wszystkich, więc napisałem tak:

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 

być używane tak:

try: 
    with Pidfile(args.pidfile): 
     process(args) 
except ProcessRunningException: 
    print "the pid file is in use, oops." 
1

Wiem, że to stary wątek, ale Stworzyłem także prostą blokadę, która opiera się tylko na rodzimych bibliotekach Pythona:

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 
Powiązane problemy