2009-09-18 57 views
21

我正在寫一個Python腳本,可能會或可能不會(取決於一堆事情)運行了很長時間,我想確保多個實例(通過cron啓動)不會踩在每個其他腳趾。這樣做的邏輯方法似乎是一個基於PID的鎖文件...但是我不想重新發明輪子,如果已經有代碼來執行此操作。Python:用於創建基於PID的鎖文件的模塊?

那麼,有沒有一個Python模塊可以管理基於PID的鎖文件的細節?

+1

我的答案在這裏也許是有趣的:[它使用套接字來創建一個lo即變爲即使處理被髮送一個SIGKILL遠CK文件 - ] [1] [1]:http://stackoverflow.com/questions/788411/check-to-see-if-python-腳本是運行/ 7758075#7758075 – aychedee 2011-11-09 11:15:30

回答

8

如果你可以使用了GPLv2,水銀有一個模塊:

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

感謝所有其他有用的答案,但這原來是最簡單的解決方案,因爲添加的mercurial依賴對我來說不是問題(我只是用它「 「實用程序腳本)。 – 2009-10-18 16:54:51

+0

請注意,這個答案不適用於更新版本的mercurial庫(編寫本文時爲3.0.1); 'lock'類在init中需要'vfs'和'file'參數('timeout'是可選的)。 – ropable 2014-06-16 07:16:50

+0

'vfs'參數可以如下生成:'from mercurial import scmutil; vfs = scmutil.vfs(「/」)'。但是,依靠較大產品的內部模塊可能不是一個好主意。 – 2015-02-16 11:54:16

4

我相信你會找到必要的信息here。所討論的頁面是指用於在python中構建守護進程的包:此過程涉及創建PID鎖文件。

+0

這個模塊似乎是Python標準庫lockfile模塊的一個包裝器,它看起來對我來說是原子的。 – 2010-11-13 06:06:05

+0

非常感謝,絕對我期待。 – pylover 2012-04-17 19:36:10

+0

它是通過這個https://github.com/khertan/Khweeteur/blob/master/khweeteur/pydaemon/pidlockfile.py作爲Ben Finney的更新代碼而被分散修補在github上的。 – 2013-05-29 15:07:38

2

有一個recipe on ActiveState on creating lockfiles

要生成文件名,您可以使用os.getpid()來獲取PID。

+1

ActiveState解決方案對我而言並不重要。我認爲它需要用臨時名稱創建鎖定文件,如「lockfile。$ PID」,將PID寫入它,然後將「lockfile。$ PID」重命名爲「lockfile」。然後通過重新讀取lockfile來查看它是否有你的PID。這可能是爲了許多目的而矯枉過正,但它是最穩健的方式。 – 2010-11-13 05:59:39

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