python线程中超时信号的替代方法

python线程中超时信号的替代方法,python,multithreading,python-3.x,timeout,signals,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 Excep

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

例如:

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只在主线程中起作用

我假设带有信号的超时方法不适用于线程

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

timeout = 5
start = time.time()

while true:
    print("zzz")
    sleep(1)
    if time.time() <= start+timeout:
        print("Time's up)
        break
process\u thread
函数通过执行以下操作来处理
工具的标准输出:

for line in p.stdout:
    # process line of the processes stdout
此过程可能需要很长时间,例如,一旦
工具
不产生任何输出。我只想要
工具的输出,比如说5秒,所以for循环需要在特定的超时后被中断

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


edit3:我创建了一个更详细、更准确的示例,说明我打算如何在线程中使用信号

你要找的是一个看门狗

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

注意:未测试代码,可能有语法错误

这实现了一个
类终止符
, 在给定的
timeout=5
之后,它向
线程进程发送
信号.SIG…
。可以使用多个不同的
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()  
这是工作负载,例如:

def workload(n, sigterm):
    print('Start workload(%s)' % n)
    arg = str(n)
    p = Popen(["tool", "--param", arg], stdin=PIPE, stdout=PIPE, stderr=STDOUT)

    sigterm.signal(p, timeout=4, sig=signal.SIGTERM)
    while True:
        for line in p.stdout:
            # process line of the processes stdout
            print(line.strip())
            time.sleep(1)

        if p.poll() != None:
            break

    sigterm.close(p)
    time.sleep(0.1)
    print('Exit workload(%s)' % n)

if __name__ == '__main__':
    with Terminator() as sigterm:
        p1 = Thread(target=workload, args=(1, sigterm)); p1.start(); time.sleep(0.1)
        p2 = Thread(target=workload, args=(2, sigterm)); p2.start(); time.sleep(0.1)
        p3 = Thread(target=workload, args=(3, sigterm)); p3.start(); time.sleep(0.1)
        p1.join(); p2.join(); p3.join()

        time.sleep(0.5)
    print('EXIT __main__')

使用Python:3.4.2和Python:2.7.9进行测试

如何开始线程?您在定义它们时是否设置了
daemon=True
?如果是这样,那么当
main
线程死亡时,这些线程将被终止。这就是你想要做的吗?是的,我正在以执事的身份启动这些线程,我会在一分钟内编辑OP。不,这不是我要尝试的,我会在OP中更好地解释它。我已经更新了OP@BillyReading你的要点并不能打开我对工作负载过程的理解。但无论如何,我已经更新了我的答案。@stovfl我可以尝试创建一个更清晰的示例代码,但我会查看您的编辑,提前谢谢。我觉得我好像以前见过/使用过这个或类似的东西,但我现在无法理解它。我也发现,在研究看门狗的话题时,我仍然不知道从哪里开始。您能用我发布的代码创建一个示例吗?谢谢您的反馈和示例,但我认为这不适用于多线程,如OP中所述。我在OP中添加了一个更详细的代码片段作为链接。此外,如果我尝试将该解决方案扩展到多线程,我会遇到问题,我所有的线程都报告相同的PID。是的,它不可伸缩。在阅读您更详细的代码片段后,将返回scalabel解决方案。
def workload(n, sigterm):
    print('Start workload(%s)' % n)
    arg = str(n)
    p = Popen(["tool", "--param", arg], stdin=PIPE, stdout=PIPE, stderr=STDOUT)

    sigterm.signal(p, timeout=4, sig=signal.SIGTERM)
    while True:
        for line in p.stdout:
            # process line of the processes stdout
            print(line.strip())
            time.sleep(1)

        if p.poll() != None:
            break

    sigterm.close(p)
    time.sleep(0.1)
    print('Exit workload(%s)' % n)

if __name__ == '__main__':
    with Terminator() as sigterm:
        p1 = Thread(target=workload, args=(1, sigterm)); p1.start(); time.sleep(0.1)
        p2 = Thread(target=workload, args=(2, sigterm)); p2.start(); time.sleep(0.1)
        p3 = Thread(target=workload, args=(3, sigterm)); p3.start(); time.sleep(0.1)
        p1.join(); p2.join(); p3.join()

        time.sleep(0.5)
    print('EXIT __main__')