San*_*4ez 9 python timeout contextmanager time-limiting
我有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)
Run Code Online (Sandbox Code Playgroud)
我见过装饰器的实现,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()
Run Code Online (Sandbox Code Playgroud)
Mr *_*ooz 11
如果上下文管理器保护的代码是基于循环的,请考虑以人们处理线程查杀的方式处理它.杀死另一个线程通常是不安全的,因此标准方法是让控制线程设置一个工作线程可见的标志.工作线程定期检查该标志并干净地关闭自己.以下是您可以执行与超时类似的操作:
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
Run Code Online (Sandbox Code Playgroud)
这是一个单线程用法示例:
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)
Run Code Online (Sandbox Code Playgroud)
和一个多线程的:
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)
Run Code Online (Sandbox Code Playgroud)
这种方法比使用信号更具侵入性,但它适用于任意线程.
小智 -3
系统调用的超时是通过信号完成的。当信号发生时,大多数阻塞系统调用都会返回 EINTR,因此您可以使用警报来实现超时。
这是一个适用于大多数系统调用的上下文管理器,如果耗时太长,则会导致阻塞系统调用引发 IOError。
import signal, errno
from contextlib import contextmanager
import fcntl
@contextmanager
def timeout(seconds):
def timeout_handler(signum, frame):
pass
original_handler = signal.signal(signal.SIGALRM, timeout_handler)
try:
signal.alarm(seconds)
yield
finally:
signal.alarm(0)
signal.signal(signal.SIGALRM, original_handler)
with timeout(1):
f = open("test.lck", "w")
try:
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
except IOError, e:
if e.errno != errno.EINTR:
raise e
print "Lock timed out"
Run Code Online (Sandbox Code Playgroud)