带线程的 Python 超时上下文管理器

2024-01-22

I have timeout上下文管理器与信号完美配合,但在多线程模式下会引发错误,因为信号仅在主线程中工作。

def timeout_handler(signum, frame):
    raise TimeoutException()

@contextmanager
def timeout(seconds):
    old_handler = signal.signal(signal.SIGALRM, timeout_handler)
    signal.alarm(seconds)
    try:
        yield
    finally:
        signal.alarm(0)
        signal.signal(signal.SIGALRM, old_handler)

我见过装饰器的实现timeout但我不知道如何通过yield内部类派生自threading.Thread。我的变体不起作用。

@contextmanager
def timelimit(seconds):
    class FuncThread(threading.Thread):
        def run(self):
            yield

    it = FuncThread()        
    it.start()
    it.join(seconds)

    if it.isAlive():
        raise TimeoutException()

如果上下文管理器保护的代码是基于循环的,请考虑以人们处理线程终止的方式来处理它。杀死另一个线程通常是不安全的,因此标准方法是让控制线程设置一个对工作线程可见的标志。工作线程定期检查该标志并干净地关闭自身。以下是如何执行与超时类似的操作:

class timeout(object):
    def __init__(self, seconds):
        self.seconds = seconds
    def __enter__(self):
        self.die_after = time.time() + self.seconds
        return self
    def __exit__(self, type, value, traceback):
        pass
    @property
    def timed_out(self):
        return time.time() > self.die_after

这是一个单线程的使用示例:

with timeout(1) as t:
    while True: # this will take a long time without a timeout
        # periodically check for timeouts
        if t.timed_out:
            break # or raise an exception
        # do some "useful" work
        print "."
        time.sleep(0.2)

和一个多线程的:

import thread
def print_for_n_secs(string, seconds):
    with timeout(seconds) as t:
        while True:
            if t.timed_out:
                break # or raise an exception
            print string,
            time.sleep(0.5)

for i in xrange(5):
    thread.start_new_thread(print_for_n_secs,
                            ('thread%d' % (i,), 2))
    time.sleep(0.25)

这种方法比使用信号更具侵入性,但它适用于任意线程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

带线程的 Python 超时上下文管理器 的相关文章

随机推荐