替代python线程中的超时信号

时间:2017-03-07 12:00:47

标签: python multithreading python-3.x timeout signals

我有一个应用程序依赖于多个阻塞操作的超时信号。

例如:

def wait_timeout(signum, frame):
    raise Exception("timeout")

signal.signal(signal.SIGALRM, wait_timeout)
signal.setitimer(signal.ITIMER_REAL, 5)

try:
    while true:
        print("zzz")
        sleep(1)
except Exception as e:
    # timeout
    print("Time's up")

现在我使用相同的方法实现了多线程,但是我得到了所有线程ValueError: signal only works in main thread

我认为信号超时的方法不适用于线程。

不幸的是我不能使用这样的东西:

timeout = 5
start = time.time()

while true:
    print("zzz")
    sleep(1)
    if time.time() <= start+timeout:
        print("Time's up)
        break

由于while循环中的操作可能阻塞并且可能永远持续,因此循环可能永远不会到达if子句。

问:如何在线程中实现超时,就像我以前用信号做的那样?

编辑:我遇到了this blog post,在python中的JavaScript中显示了与setTimeout()类似的解决方案。我认为这可能是一个可能的解决方案,但我真的不确定如何使用它。

edit2:我在main中启动线程如下:

p = Popen(["tool", "--param", arg], stdin=PIPE, stdout=PIPE, stderr=STDOUT)
t = Thread(target=process_thread, daemon=True, args=(p,arg1,arg2))
t.start()

process_thread函数通过执行以下操作来处理tool的标准输出:

for line in p.stdout:
    # process line of the processes stdout

此过程可能需要永久,例如一旦tool没有产生任何输出。我只想要tool的输出,比方说5秒,所以在特定的超时后,for循环需要被打破。

这就是我使用信号的方式,但显然它们在线程中不起作用。

edit3:我已经创建了一个更详细和准确的示例,说明我打算如何在线程中使用singals。 See the gist here

2 个答案:

答案 0 :(得分:2)

您正在寻找的是看门狗

def watchdog(queue):
    while True:
        watch = queue.get()
        time.sleep(watch.seconds)

        try:
            watch = queue.get_nowait()
            # No except, got queue message, 
            #   do noting wait for next watch

        except queue.Empty:
            os.kill(watch.pid, signal.SIGKILL)

def workload_thread(queue):
    pid = os.getpid()
    queue.put({'pid':pid, 'seconds':5})

    # do your work
    # Test Watchdog
    # time.sleep(6)

    queue.put({'pid':pid, 'done':True})

注意:代码未经过测试,可能会出现语法错误!

答案 1 :(得分:1)

这实现了.nav img:hover + ul li { display: block; } , 在给定的class Terminatorsignal.SIG...之后发送timeout=5。多个不同的Threads Popen process是可能的。

pid

这是工作量:

class Terminator(object):
    class WObj():
        def __init__(self, process, timeout=0, sig=signal.SIGABRT):
            self.process = process
            self.timeout = timeout
            self.sig = sig

    def __init__(self):
        self.__queue = queue.Queue()
        self.__t = Thread(target=self.__sigterm_thread, args=(self.__queue,))
        self.__t.start()
        time.sleep(0.1)

    def __sigterm_thread(self, q):
        w = {}
        t = 0
        while True:
            time.sleep(0.1);
            t += 1
            try:
                p = q.get_nowait()
                if p.process == 0 and p.sig == signal.SIGTERM:
                    # Terminate sigterm_thread
                    return 1

                if p.process.pid not in w:
                    if p.timeout > 0 and p.sig != signal.SIGABRT:
                        w[p.process.pid] = p
                else:
                    if p.sig == signal.SIGABRT:
                        del (w[p.process.pid])
                    else:
                        w[p.process.pid].timeout = p.timeout

            except queue.Empty:
                pass

            if t == 10:
                for key in list(w.keys()):
                    p = w[key]
                    p.timeout -= 1
                    if p.timeout == 0:
                        """ A None value indicates that the process hasn't terminated yet. """
                        if p.process.poll() == None:
                            p.process.send_signal(p.sig)
                        del (w[p.process.pid])
                t = 0
            # end if t == 10
        # end while True

    def signal(self, process, timeout=0, sig=signal.SIGABRT):
        self.__queue.put(self.WObj(process, timeout, sig))
        time.sleep(0.1)

    def close(self, process):
        self.__queue.put(self.WObj(process, 0, signal.SIGABRT))
        time.sleep(0.1)

    def terminate(self):
        while not self.__queue.empty():
            trash = self.__queue.get()

        if self.__t.is_alive():
            self.__queue.put(self.WObj(0, 0, signal.SIGTERM))

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.__del__()

    def __del__(self):
        self.terminate()  

使用Python测试:3.4.2和Python:2.7.9