2011-07-01 6 views
7

Tôi đang sử dụng một công thức dựa trên SIGALRM để thiết lập ngắt báo động - Using module 'subprocess' with timeoutNhiều subprocesses với timeouts

Vấn đề là tôi có nhiều hơn một kịch bản Python sử dụng quy trình signal.ALARM để thiết lập timeout và chỉ báo thức mới nhất được gọi. Cách tốt nhất để cải thiện thời gian cài đặt chức năng nhiều Python này là gì?

Trả lời

11

Ngoại trừ các hacks đơn giản, nhanh chóng, tránh SIGALRM. Đó là một cơ chế rất hạn chế, không phù hợp với bất cứ điều gì phức tạp hơn: bạn chỉ có thể đặt một báo thức duy nhất và nó sẽ ngắt bất kỳ cuộc gọi hệ thống nào vào thời điểm đó thay vì chỉ là lệnh bạn định ngắt.

Đó là sạch hơn để sử dụng một sợi thời gian chờ để giết chết quá trình, ví dụ:

import subprocess, signal, os, threading, errno 
from contextlib import contextmanager 

class TimeoutThread(object): 
    def __init__(self, seconds): 
     self.seconds = seconds 
     self.cond = threading.Condition() 
     self.cancelled = False 
     self.thread = threading.Thread(target=self._wait) 

    def run(self): 
     """Begin the timeout.""" 
     self.thread.start() 

    def _wait(self): 
     with self.cond: 
      self.cond.wait(self.seconds) 

      if not self.cancelled: 
       self.timed_out() 

    def cancel(self): 
     """Cancel the timeout, if it hasn't yet occured.""" 
     with self.cond: 
      self.cancelled = True 
      self.cond.notify() 
     self.thread.join() 

    def timed_out(self): 
     """The timeout has expired.""" 
     raise NotImplementedError 

class KillProcessThread(TimeoutThread): 
    def __init__(self, seconds, pid): 
     super(KillProcessThread, self).__init__(seconds) 
     self.pid = pid 

    def timed_out(self): 
     try: 
      os.kill(self.pid, signal.SIGKILL) 
     except OSError as e: 
      # If the process is already gone, ignore the error. 
      if e.errno not in (errno.EPERM, errno. ESRCH): 
       raise e 

@contextmanager 
def processTimeout(seconds, pid): 
    timeout = KillProcessThread(seconds, pid) 
    timeout.run() 
    try: 
     yield 
    finally: 
     timeout.cancel() 


def example(): 
    proc = subprocess.Popen(["sleep", "5"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) 

    with processTimeout(1, proc.pid): 
     print proc.communicate() 

    resultcode = proc.wait() 
    if resultcode < 0: 
     print "error: %i" % resultcode 

if __name__ == '__main__': 
    example() 

Tùy thuộc vào những gì bạn đang thời gian ra ngoài, bạn có thể muốn sử dụng một tín hiệu nhẹ hơn SIGKILL để cho phép thời gian -quá trình để tự dọn dẹp.