使用python的线程不工作超时功能(Timeout function using threadin

2019-07-04 22:59发布

我发现了一个代码创建一个超时功能在这里 ,这似乎并没有工作。 完整的测试代码如下:

def timeout(func, args=(), kwargs={}, timeout_duration=1, default=None):
    import threading
    class InterruptableThread(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.result = None

        def run(self):
            try:
                self.result = func(*args, **kwargs)
            except:
                self.result = default

    it = InterruptableThread()
    it.start()
    it.join(timeout_duration)
    if it.isAlive():
        return default
    else:
        return it.result


def foo():
    while True:
        pass

timeout(foo,timeout_duration=3)

预期的行为:在3秒内码结束。 问题出在哪儿?

Answer 1:

一个线程将无法正常杀死另一个线程,所以你当前的代码, foo永远不会终止。 (随着thread.daemon = True当只有守护线程留在Python程序将退出,但是,这并不让你终止foo而不同时终止主线程。)

有些人曾试图使用信号来停止执行,但是这可能是不安全的在某些情况下。

如果你可以修改foo ,有很多解决方案成为可能。 举例来说,你可以检查一个threading.Event打出来的,而循环的。

但是,如果你不能修改foo ,你可以在使用一个子进程运行multiprocessing因为与线程模块,子过程可以终止。 下面是如何可能看一个例子:

import time
import multiprocessing as mp

def foo(x = 1):
    cnt = 1
    while True:
        time.sleep(1)
        print(x, cnt)
        cnt += 1

def timeout(func, args = (), kwds = {}, timeout = 1, default = None):
    pool = mp.Pool(processes = 1)
    result = pool.apply_async(func, args = args, kwds = kwds)
    try:
        val = result.get(timeout = timeout)
    except mp.TimeoutError:
        pool.terminate()
        return default
    else:
        pool.close()
        pool.join()
        return val


if __name__ == '__main__':
    print(timeout(foo, kwds = {'x': 'Hi'}, timeout = 3, default = 'Bye'))
    print(timeout(foo, args = (2,), timeout = 2, default = 'Sayonara'))

产量

('Hi', 1)
('Hi', 2)
('Hi', 3)
Bye
(2, 1)
(2, 2)
Sayonara

请注意,这有一定的局限性太大。

  • 子进程收到父进程的变量的副本 。 如果你在一个子进程修改变量,也不会影响父进程。 如果你的函数func需要修改的变量,你需要使用一个共享变量 。

  • 参数(穿过args )和关键字( kwds )必须picklable。

  • 过程是资源更重比线程。 通常情况下,你只需要在程序开始创建一次多池。 此timeout函数创建一个Pool每次调用它。 因为我们需要这样做是必要pool.terminate()终止foo 。 有可能是一个更好的办法,但我没有想到这一点。


Answer 2:

你需要把it变成一个守护线程 :

it = ...
it.daemon = True
it.start()

否则,它的创建为用户线程,进程不会得到停止,直到所有的用户线程完成。

请注意,您的实现线程将继续运行,消耗你已经超时等待它即使资源。 CPython中的全局解释器锁可能进一步加剧的问题。



文章来源: Timeout function using threading in python does not work