gpt4 book ai didi

python - 为什么我可以将实例方法传递给 multiprocessing.Process,而不是 multiprocessing.Pool?

转载 作者:IT老高 更新时间:2023-10-28 21:45:26 28 4
gpt4 key购买 nike

我正在尝试编写一个与 multiprocessing.Pool 同时应用函数的应用程序。我希望这个函数成为一个实例方法(所以我可以在不同的子类中以不同的方式定义它)。这似乎是不可能的;正如我在其他地方了解到的,显然是 bound methods can't be pickled .那么为什么以绑定(bind)方法作为目标来启动 multiprocessing.Process 呢?以下代码:

import multiprocessing

def test1():
print "Hello, world 1"

def increment(x):
return x + 1

class testClass():
def process(self):
process1 = multiprocessing.Process(target=test1)
process1.start()
process1.join()
process2 = multiprocessing.Process(target=self.test2)
process2.start()
process2.join()

def pool(self):
pool = multiprocessing.Pool(1)
for answer in pool.imap(increment, range(10)):
print answer
print
for answer in pool.imap(self.square, range(10)):
print answer

def test2(self):
print "Hello, world 2"

def square(self, x):
return x * x

def main():
c = testClass()
c.process()
c.pool()

if __name__ == "__main__":
main()

产生这个输出:

Hello, world 1
Hello, world 2
1
2
3
4
5
6
7
8
9
10

Exception in thread Thread-2:
Traceback (most recent call last):
File "C:\Python27\Lib\threading.py", line 551, in __bootstrap_inner
self.run()
File "C:\Python27\Lib\threading.py", line 504, in run
self.__target(*self.__args, **self.__kwargs)
File "C:\Python27\Lib\multiprocessing\pool.py", line 319, in _handle_tasks
put(task)
PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup __builtin__.instancemethod failed

为什么进程可以处理绑定(bind)方法,但池不能?

最佳答案

pickle 模块通常不能pickle 实例方法:

>>> import pickle
>>> class A(object):
... def z(self): print "hi"
...
>>> a = A()
>>> pickle.dumps(a.z)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/pickle.py", line 1374, in dumps
Pickler(file, protocol).dump(obj)
File "/usr/local/lib/python2.7/pickle.py", line 224, in dump
self.save(obj)
File "/usr/local/lib/python2.7/pickle.py", line 306, in save
rv = reduce(self.proto)
File "/usr/local/lib/python2.7/copy_reg.py", line 70, in _reduce_ex
raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle instancemethod objects

然而,multiprocessing 模块has a custom Pickler that adds some code to enable this feature :

#
# Try making some callable types picklable
#

from pickle import Pickler
class ForkingPickler(Pickler):
dispatch = Pickler.dispatch.copy()

@classmethod
def register(cls, type, reduce):
def dispatcher(self, obj):
rv = reduce(obj)
self.save_reduce(obj=obj, *rv)
cls.dispatch[type] = dispatcher

def _reduce_method(m):
if m.im_self is None:
return getattr, (m.im_class, m.im_func.func_name)
else:
return getattr, (m.im_self, m.im_func.func_name)
ForkingPickler.register(type(ForkingPickler.save), _reduce_method)

您可以使用 copy_reg 复制它模块来看看它是否适合自己:

>>> import copy_reg
>>> def _reduce_method(m):
... if m.im_self is None:
... return getattr, (m.im_class, m.im_func.func_name)
... else:
... return getattr, (m.im_self, m.im_func.func_name)
...
>>> copy_reg.pickle(type(a.z), _reduce_method)
>>> pickle.dumps(a.z)
"c__builtin__\ngetattr\np0\n(ccopy_reg\n_reconstructor\np1\n(c__main__\nA\np2\nc__builtin__\nobject\np3\nNtp4\nRp5\nS'z'\np6\ntp7\nRp8\n."

当您使用 Process.start 在 Windows 上生成新进程时,it pickles all the parameters you passed to the child process using this custom ForkingPickler :

#
# Windows
#

else:
# snip...
from pickle import load, HIGHEST_PROTOCOL

def dump(obj, file, protocol=None):
ForkingPickler(file, protocol).dump(obj)

#
# We define a Popen class similar to the one from subprocess, but
# whose constructor takes a process object as its argument.
#

class Popen(object):
'''
Start a subprocess to run the code of a process object
'''
_tls = thread._local()

def __init__(self, process_obj):
# create pipe for communication with child
rfd, wfd = os.pipe()

# get handle for read end of the pipe and make it inheritable
...
# start process
...

# set attributes of self
...

# send information to child
prep_data = get_preparation_data(process_obj._name)
to_child = os.fdopen(wfd, 'wb')
Popen._tls.process_handle = int(hp)
try:
dump(prep_data, to_child, HIGHEST_PROTOCOL)
dump(process_obj, to_child, HIGHEST_PROTOCOL)
finally:
del Popen._tls.process_handle
to_child.close()

请注意“向 child 发送信息”部分。它使用 dump 函数,该函数使用 ForkingPickler 来 pickle 数据,这意味着您的实例方法可以被 pickle 。

现在,当您使用 multiprocessing.Pool 上的方法将方法发送到子进程时,它使用 multiprocessing.Pipe 来 pickle 数据。在 Python 2.7 中,multiprocessing.Pipe 用 C 语言实现,and calls pickle_dumps directly ,所以它没有利用 ForkingPickler。这意味着 pickle 实例方法不起作用。

但是,如果你使用 copy_reg 来注册 instancemethod 类型,而不是自定义 Picklerall pickle 的尝试会受到影响。所以你可以使用它来启用 pickle 实例方法,甚至通过 Pool:

import multiprocessing
import copy_reg
import types

def _reduce_method(m):
if m.im_self is None:
return getattr, (m.im_class, m.im_func.func_name)
else:
return getattr, (m.im_self, m.im_func.func_name)
copy_reg.pickle(types.MethodType, _reduce_method)

def test1():
print("Hello, world 1")

def increment(x):
return x + 1

class testClass():
def process(self):
process1 = multiprocessing.Process(target=test1)
process1.start()
process1.join()
process2 = multiprocessing.Process(target=self.test2)
process2.start()
process2.join()

def pool(self):
pool = multiprocessing.Pool(1)
for answer in pool.imap(increment, range(10)):
print(answer)
print
for answer in pool.imap(self.square, range(10)):
print(answer)

def test2(self):
print("Hello, world 2")

def square(self, x):
return x * x

def main():
c = testClass()
c.process()
c.pool()

if __name__ == "__main__":
main()

输出:

Hello, world 1
Hello, world 2
GOT (0, 0, (True, 1))
GOT (0, 1, (True, 2))
GOT (0, 2, (True, 3))
GOT (0, 3, (True, 4))
GOT (0, 4, (True, 5))
1GOT (0, 5, (True, 6))

GOT (0, 6, (True, 7))
2
GOT (0, 7, (True, 8))
3
GOT (0, 8, (True, 9))
GOT (0, 9, (True, 10))
4
5
6
7
8
9
10

GOT (1, 0, (True, 0))
0
GOT (1, 1, (True, 1))
1
GOT (1, 2, (True, 4))
4
GOT (1, 3, (True, 9))
9
GOT (1, 4, (True, 16))
16
GOT (1, 5, (True, 25))
25
GOT (1, 6, (True, 36))
36
GOT (1, 7, (True, 49))
49
GOT (1, 8, (True, 64))
64
GOT (1, 9, (True, 81))
81
GOT None

另外请注意,在 Python 3.x 中,pickle 可以原生地 pickle 实例方法类型,因此这些东西都不再重要了。 :)

关于python - 为什么我可以将实例方法传递给 multiprocessing.Process,而不是 multiprocessing.Pool?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27318290/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com