Mag*_*ero 5 python pickle python-multiprocessing process-pool
为什么以下使用该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()
Run Code Online (Sandbox Code Playgroud)
这个调用产生了一个无形的例外[Errno 24] Too many open files(见它,更换线路executor.submit(super().f)用print(executor.submit(super().f).exception()))。
但是,按预期将打印替换ProcessPoolExecutor为ThreadPoolExecutor“被调用”。
为什么以下使用该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()
Run Code Online (Sandbox Code Playgroud)
但是,按预期将打印替换Pool为ThreadPool“被调用”。
环境:CPython 3.7,MacOS 10.14。
concurrent.futures.ProcessPoolExecutor并multiprocessing.pool.Pool用于multiprocessing.queues.Queue将工作函数对象从调用者传递到工作进程,Queue使用pickle模块进行序列化/反序列化,但未能正确处理带有子类实例的绑定方法对象:
f = super().f
print(f)
pf = pickle.loads(pickle.dumps(f))
print(pf)
Run Code Online (Sandbox Code Playgroud)
输出:
<bound method A.f of <__main__.B object at 0x104b24da0>>
<bound method B.f of <__main__.B object at 0x104cfab38>>
Run Code Online (Sandbox Code Playgroud)
A.f变成B.f,这有效B.f地B.f在worker进程中创建了无限递归调用。
pickle.dumps利用__reduce__IMO方法的绑定方法对象IMO 的实现,不考虑这种情况,这种情况不涉及实际func对象,而只是尝试从具有简单名称()的实例selfobj(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)
Run Code Online (Sandbox Code Playgroud)
我们可以这样做,因为绑定方法是一个描述符。
ps:我已经提交了错误报告。
| 归档时间: |
|
| 查看次数: |
544 次 |
| 最近记录: |