2009-09-18 9 views
21

私はPythonスクリプトを書いていますが、これは長い間実行されるかもしれませんが、複数のインスタンス(cronを介して起動される)がそれぞれにステップしないようにしたいと思います他のつま先。これを行うための論理的な方法は、PIDベースのロックファイルであるようです...しかし、これを行うためのコードが既に存在する場合は、ホイールを再作成したくありません。Python:PIDベースのロックファイルを作成するためのモジュールですか?

したがって、PIDベースのロックファイルの詳細を管理するPythonモジュールがありますか?

+1

私の答えはここでも興味があるかもしれません:プロセスはSIGKILLを送信した場合であっても消えるCKファイル - ] [1] [1]:http://stackoverflow.com/questions/788411/check-to-see-if-python-スクリプト実行中/ 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

他の有益な回答をありがとうが、これは最も簡単な解決策であることが判明した。追加されたmercurial依存関係は私にとっては問題ではない(私はちょっとした"ユーティリティスクリプト)。 –

+0

この答えは、新しいバージョンのmercurialライブラリ(3.0.1時点)では機能しません。 'lock'クラスはinit(' timeout'はオプション)上の 'vfs'と' file'引数の両方を期待しています。 – ropable

+0

'vfs'引数は以下のように生成できます:' from mercurial import scmutil; vfs = scmutil.vfs( "/") 'しかし、より大きな製品の内部モジュールに頼ることは、おそらくそれほど良い考えではありません。 –

4

Iあなたが必要な情報を見つけると信じてhere。問題のページは、Pythonでデーモンを構築するためのパッケージを参照しています。このプロセスではPIDロックファイルを作成します。

+0

このモジュールは、Pythonの標準ライブラリlockfileモジュールの上にあるラッパーのようです。 –

+0

ありがとう、絶対に私はそれを期待しています。 – pylover

+0

これは、https://github.com/khertan/Khweeteur/blob/master/khweeteur/pydaemon/pidlockfile.pyをBen Finneyのより新しいコードとして、githubに散布パッチされています。 –

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 
関連する問題