【问题标题】:Why do ProcessPoolExecutor and Pool crash with a super() call?为什么 ProcessPoolExecutor 和 Pool 在调用 super() 时会崩溃?
【发布时间】:2019-10-29 18:43:48
【问题描述】:

1.为什么以下使用 concurrent.futures 模块的 Python 代码永远挂起?

import concurrent.futures


class A:

    def f(self):
        print("called")


class B(A):

    def f(self):
        executor = concurrent.futures.ProcessPoolExecutor(max_workers=2)
        executor.submit(super().f)


if __name__ == "__main__":
    B().f()

调用引发了一个不可见的异常[Errno 24] Too many open files(要查看它,请将executor.submit(super().f) 行替换为print(executor.submit(super().f).exception()))。

但是,将 ProcessPoolExecutor 替换为 ThreadPoolExecutor 会按预期打印“调用”。

2。为什么以下使用 multiprocessing.pool 模块的 Python 代码会引发异常 AssertionError: daemonic processes are not allowed to have children

import multiprocessing.pool


class A:

    def f(self):
        print("called")


class B(A):

    def f(self):
        pool = multiprocessing.pool.Pool(2)
        pool.apply(super().f)


if __name__ == "__main__":
    B().f()

但是,将 Pool 替换为 ThreadPool 会按预期打印“调用”。

环境:CPython 3.7、MacOS 10.14。

【问题讨论】:

    标签: python pickle python-multiprocessing process-pool


    【解决方案1】:

    concurrent.futures.ProcessPoolExecutormultiprocessing.pool.Pool 使用multiprocessing.queues.Queue 将工作函数对象从调用者传递给工作进程,Queue 使用pickle 模块进行序列化/反序列化,但它未能正确处理带有子的绑定方法对象类实例:

    f = super().f
    print(f)
    pf = pickle.loads(pickle.dumps(f))
    print(pf)
    

    输出:

    <bound method A.f of <__main__.B object at 0x104b24da0>>
    <bound method B.f of <__main__.B object at 0x104cfab38>>
    

    A.f 变为 B.f,这实际上会在工作进程中创建无限递归调用 B.fB.f

    pickle.dumps利用绑定方法对象的__reduce__方法,IMO,its implementation,没有考虑这个场景,不处理真正的func对象,只是尝试从实例中取回self obj (B()) 具有简单的名称 (f),导致 B.f,很可能是一个错误。

    好消息是,我们知道问题出在哪里,我们可以通过实现我们自己的归约函数来解决它,该函数尝试从原始函数 (A.f) 和实例 obj (B()) 重新创建绑定的方法对象:

    import types
    import copyreg
    import multiprocessing
    
    def my_reduce(obj):
        return (obj.__func__.__get__, (obj.__self__,))
    
    copyreg.pickle(types.MethodType, my_reduce)
    multiprocessing.reduction.register(types.MethodType, my_reduce)
    

    我们可以这样做,因为绑定方法是一个描述符。

    ps:我已提交a bug report

    【讨论】:

    • 一个字:令人印象深刻!
    • Python 解决方法multiprocessing.reduction.register(types.MethodType, my_reduce) 存在问题:如果您尝试实例化multiprocessing.Manager(),则会收到此异常:_pickle.PicklingError: Can't pickle &lt;function BaseManager._run_server at 0x00000198B0A91D90&gt;: it's not the same object as multiprocessing.managers.BaseManager._run_server。不过,它似乎只影响 Windows。
    猜你喜欢
    • 1970-01-01
    • 2012-05-13
    • 2011-04-17
    • 1970-01-01
    • 2012-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多