2009-09-18 14 views
21

Tôi đang viết một tập lệnh Python có thể hoặc không thể (tùy thuộc vào một loạt các thứ) chạy trong một thời gian dài, và tôi muốn đảm bảo rằng nhiều phiên bản (bắt đầu qua cron) không thực hiện từng bước những ngón chân khác. Cách hợp lý để làm điều này có vẻ là một lockfile dựa trên PID… Nhưng tôi không muốn tái phát minh ra bánh xe nếu đã có mã để làm điều này.Python: mô-đun để tạo lockfile dựa trên PID?

Vì vậy, có một mô-đun Python nào ở đó sẽ quản lý chi tiết của một tệp khóa dựa trên PID không?

+1

Câu trả lời của tôi ở đây cũng có thể quan tâm: [Nó sử dụng ổ cắm để tạo tệp ck sẽ biến mất ngay cả khi quá trình được gửi bằng sigkill -] [1] [1]: http://stackoverflow.com/questions/788411/check-to-see-if-python- script-is-running/7758075 # 7758075 – aychedee

Trả lời

8

Nếu bạn có thể sử dụng GPLv2, Mercurial có một mô-đun cho rằng:

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

Ví dụ sử dụng:

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

Cảm ơn tất cả các câu trả lời hữu ích khác, nhưng điều này hóa ra lại là giải pháp đơn giản nhất, vì sự phụ thuộc thêm vào thủy ngân không phải là vấn đề đối với tôi (tôi chỉ sử dụng nó cho "ít" "tập lệnh tiện ích). –

+0

Lưu ý rằng câu trả lời này không hoạt động với các phiên bản mới hơn của thư viện thủy ngân (3.0.1 tại thời điểm viết); lớp 'lock' mong đợi cả' vfs' và 'file' args trên init (' timeout' là tùy chọn). – ropable

+0

Đối số 'vfs' có thể được tạo như sau:' từ scmutil nhập khẩu thủy ngân; vfs = scmutil.vfs ("/") '. Tuy nhiên, dựa vào một mô-đun nội bộ của một sản phẩm lớn hơn có lẽ không phải là một ý tưởng hay. –

4

tôi tin rằng bạn sẽ tìm thấy thông tin cần thiết here. Trang được đề cập là một gói để xây dựng các trình tiện ích trong python: quá trình này liên quan đến việc tạo một tệp khóa PID.

+0

Mô-đun này có vẻ là một trình bao bọc trên mô-đun khóa thư viện chuẩn của thư viện Python, có vẻ như nó là nguyên tử đối với tôi. –

+0

cảm ơn rất nhiều, hoàn toàn tôi mong đợi nó. – pylover

+0

Nó được phân tán trên github với https://github.com/khertan/Khweeteur/blob/master/khweeteur/pydaemon/pidlockfile.py này là một mã mới hơn của Ben Finney. –

2

Có một số recipe on ActiveState on creating lockfiles.

Để tạo tên tệp, bạn có thể sử dụng os.getpid() để lấy PID.

+1

Các giải pháp ActiveState không nhìn nguyên tử với tôi. Tôi nghĩ rằng nó cần phải tạo ra các tập tin khóa với một tên tạm thời, như "lockfile. $ PID", ghi PID vào nó, sau đó đổi tên "lockfile. $ PID" thành "lockfile". Sau đó kiểm tra bằng cách đọc lại tệp khóa để xem liệu nó có PID của bạn hay không. Đây có lẽ là quá mức cần thiết cho nhiều mục đích, nhưng đó là cách mạnh mẽ nhất. –

1

tôi đã khá hài lòng với tất cả những người, vì vậy tôi đã viết này:

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 

sẽ được sử dụng một cái gì đó như thế này:

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

Tôi biết đây là một chủ đề cũ, nhưng Tôi cũng đã tạo một khóa đơn giản chỉ dựa trên thư viện gốc 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