因此,在Windows上,signal
和thread
一般都是错误的想法/不适用于功能超时。
我已经制作了以下超时代码,当代码耗时时,会从timeout exception
引发multiprocessing
。这正是我想要的。
def timeout(timeout, func, *arg):
with Pool(processes=1) as pool:
result = pool.apply_async(func, (*arg,))
return result.get(timeout=timeout)
我现在正试图将它变成装饰器样式,以便我可以将它添加到各种功能中,特别是那些调用外部服务并且我无法控制代码或持续时间的功能。我目前的尝试如下:
class TimeWrapper(object):
def __init__(self, timeout=10):
"""Timing decorator"""
self.timeout = timeout
def __call__(self, f):
def wrapped_f(*args):
with Pool(processes=1) as pool:
result = pool.apply_async(f, (*args,))
return result.get(timeout=self.timeout)
return wrapped_f
它出现了酸洗错误:
@TimeWrapper(7)
def func2(x, y):
time.sleep(5)
return x*y
File "C:\Users\rmenk\AppData\Local\Continuum\anaconda3\lib\multiprocessing\reduction.py", line 51, in dumps
cls(buf, protocol).dump(obj)
_pickle.PicklingError: Can't pickle <function func2 at 0x000000770C8E4730>: it's not the same object as __main__.func2
我怀疑这是由于多处理和装饰师不好玩但我实际上并不知道如何让它们玩得很好。关于如何解决这个问题的任何想法?
PS:我已经对这个网站和其他地方进行了一些广泛的研究,但是没有找到任何有用的答案,无论是卵石,线程,作为功能装饰器还是其他。如果你有一个解决方案,你知道在Windows和python 3.5上工作,我很乐意使用它。答案 0 :(得分:2)
您尝试实现的功能在Windows中尤为繁琐。核心问题是当你修饰一个函数时,你会影响它。这种情况在UNIX中运行得很好,因为它使用fork
策略来创建新进程。
在Windows中,新流程将是一个空白的流程,其中启动全新的Python解释器并加载您的模块。当模块加载时,装饰器隐藏了真正的功能,使得很难找到pickle
协议。
正确的唯一方法是依靠在装修期间设置的蹦床功能。你可以看看pebble
是如何完成的,但是,只要你没有进行练习,我建议直接使用pebble
,因为它已经提供了你正在寻找的东西对
from pebble import concurrent
@concurrent.process(timeout=60)
def my_function(var, keyvar=0):
return var + keyvar
future = my_function(1, keyvar=2)
future.result()
答案 1 :(得分:0)
这里唯一的问题是您在 main 上下文中测试了修饰的功能。将其移至其他模块,可能会起作用。
我写了wrapt_timeout_decorator,它使用了wrapt&dill&multiprocess&pipe和pickle&multiprocessing&queue,因为它可以序列化更多数据类型。
乍一看可能很简单,但是在Windows下,可靠的超时装饰器非常棘手-您可能会使用我的,它相当成熟且经过测试:
https://github.com/bitranox/wrapt_timeout_decorator
在Windows上,再次导入主模块(但名称为!='main'),因为Python试图在不支持分支的系统上模拟类似分支的行为。多重处理试图通过使用不同的名称再次导入主模块来创建与您的主过程相似的环境。因此,您需要使用著名的“ if if name ==' main ':”
来屏蔽程序的入口点。import lib_foo
def some_module():
lib_foo.function_foo()
def main():
some_module()
# here the subprocess stops loading, because __name__ is NOT '__main__'
if __name__ = '__main__':
main()
这是Windows操作系统的问题,因为Windows操作系统不支持“ fork”
您可以在此处找到更多信息:
Workaround for using __name__=='__main__' in Python multiprocessing
https://docs.python.org/2/library/multiprocessing.html#windows
由于main.py再次以不同的名称加载,但名称为“ main ”,因此修饰的函数现在指向不再存在的对象,因此您需要将修饰的类和函数放入另一个模块。通常(尤其是在Windows上),main()程序除了main函数外不应有其他任何东西,真正的事情应该在模块中发生。我还习惯于将所有设置或配置放在一个不同的文件中,以便所有进程或线程都可以访问它们(也可以将它们放在一个位置,不要忘记在您喜欢的编辑器中键入提示和命名完成)
“莳萝”序列化程序还可以序列化 main 上下文,这意味着我们示例中的对象被腌制为“ main .lib_foo”,“ main .some_module”,“ main .main”等。当使用“ pickle”时,由于“ pickle”无法序列化以下类型,因此我们将不受此限制:>
具有yields的函数,嵌套函数,lambda,单元格,方法,未绑定方法,模块,代码,methodwrapper,dictproxy,methoddescriptor,getsetdescriptor,memberdescriptor,wrapperdescriptor,xrange,slice,未实现,省略号,退出
其他莳萝支持:
保存并加载python解释器会话,保存并从函数和类中提取源代码,以交互方式诊断酸洗错误
要使用装饰器支持更多类型,我们选择了莳萝作为序列化器,但缺点是方法和类不能在 main 上下文中进行装饰,而是需要驻留在模块中。< / p>
您可以在这里找到更多信息:Serializing an object in __main__ with pickle or dill