【问题标题】:A timeout decorator class with multiprocessing gives a pickling error具有多处理的超时装饰器类会产生酸洗错误
【发布时间】:2018-04-20 07:53:55
【问题描述】:

所以在 Windows 上,signalthread 方法通常是坏主意/不适用于函数超时。

我制作了以下超时代码,当代码花费很长时间时,它会从 multiprocessing 抛出 timeout exception。这正是我想要的。

 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:我在这个网站和其他地方做了一些广泛的研究,但没有找到任何可行的答案,无论是使用 pebble、线程、作为函数装饰器还是其他方式。如果你有一个你知道适用于 windows 和 python 3.5 的解决方案,我很乐意使用它。

【问题讨论】:

    标签: python multiprocessing decorator


    【解决方案1】:

    您要实现的目标在 Windows 中特别繁琐。核心问题是,当你装饰一个函数时,你会隐藏它。由于它使用fork 策略来创建新进程,因此这恰好在 UNIX 中运行良好。

    不过,在 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()
    

    【讨论】:

      【解决方案2】:

      您在这里遇到的唯一问题是您在 ma​​in 上下文中测试了修饰函数。将它移到不同的模块,它可能会工作。

      我编写了 wrapt_timeout_decorator,它使用 wrapt & dill & multiprocess & pipeline 与 pickle & multiprocessing & queue 相比,因为它可以序列化更多数据类型。

      一开始可能看起来很简单,但在 windows 下,一个可靠的超时装饰器是相当棘手的 - 你可以使用我的,它非常成熟且经过测试:

      https://github.com/bitranox/wrapt_timeout_decorator

      在 Windows 上,主模块再次导入(但名称为 !='main'),因为 Python 试图在不支持分叉的系统上模拟类似分叉的行为。 multiprocessing 尝试通过使用不同名称再次导入主模块来创建类似于您的主进程的环境。这就是为什么你需要用著名的“if name == 'ma​​in':”来屏蔽你的程序的入口点:“

      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 以不同的名称再次加载,但“ma​​in”,装饰函数现在指向不再存在的对象,因此您需要将装饰的类和函数放入另一个模块。一般来说(尤其是在 Windows 上), main() 程序除了 main 函数应该没有任何东西,真正的事情应该发生在模块中。我还习惯于将所有设置或配置放在不同的文件中 - 因此所有进程或线程都可以访问它们(并将它们放在一个地方,不要忘记在您最喜欢的编辑器中输入提示和名称完成)

      “dill”序列化器还能够序列化 ma​​in 上下文,这意味着我们示例中的对象被腌制为“ma​​in.lib_foo”、“main.some_module","ma​​in.main" 等。我们在使用“pickle”时没有这个限制,缺点是“pickle”不能序列化以下类型:

      具有产量的函数、嵌套函数、lambdas、单元格、方法、unboundmethod、模块、代码、methodwrapper、dictproxy、methoddescriptor、getsetdescriptor、memberdescriptor、wrapperdescriptor、xrange、切片、未实现、省略号、退出

      额外的莳萝支持:

      保存和加载python解释器会话,保存和提取函数和类的源代码,交互式诊断酸洗错误

      为了使用装饰器支持更多类型,我们选择了 dill 作为序列化器,其小缺点是方法和类不能在 ma​​in 上下文中进行装饰,而是需要驻留在模块中。

      您可以在此处找到更多信息:Serializing an object in __main__ with pickle or dill

      【讨论】:

      • 太棒了,我没有从事导致原始问题的项目,但下次我遇到这个问题时,我会尝试你的解决方案。
      猜你喜欢
      • 2018-07-26
      • 1970-01-01
      • 2015-02-22
      • 2021-04-29
      • 2015-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多