【问题标题】:Multiple subprocesses with timeouts多个子进程超时
【发布时间】:2011-09-27 01:27:45
【问题描述】:

我正在使用一个依赖 SIGALRM 来设置警报中断的配方—— Using module 'subprocess' with timeout

问题是我有多个 Python 脚本使用 signal.ALARM 进程来设置超时,并且只调用最新的警报。有什么好的方法可以改善这种设置超时的多个 Python 函数?

【问题讨论】:

    标签: python signals


    【解决方案1】:

    除了简单、快速的 hack,避免使用 SIGALRM。这是一种非常古老、有限的机制,不适合更复杂的事情:您只能设置一个警报,并且它会在当时中断任何系统调用,而不仅仅是您打算中断的系统调用。

    使用超时线程杀死进程要干净得多,例如:

    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()
    

    根据您要超时的内容,您可能希望使用比 SIGKILL 更轻的信号,以允许超时进程自行清理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-13
      • 2011-04-22
      • 1970-01-01
      • 2016-08-25
      相关资源
      最近更新 更多