I am trying to write an application that applies a function concurrently with a multiprocessing.Pool
. I would like this function to be an instance method (so I can define it differently in different subclasses). This doesn't seem to be possible; as I have learned elsewhere, apparently bound methods can't be pickled. So why does starting a multiprocessing.Process
with a bound method as a target work? The following code:
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()
Produces this output:
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
Why can Processes handle bound methods, but not Pools?
Best Solution
The
pickle
module normally can't pickle instance methods:However, the
multiprocessing
module has a customPickler
that adds some code to enable this feature:You can replicate this using the
copy_reg
module to see it work for yourself:When you use
Process.start
to spawn a new process on Windows, it pickles all the parameters you passed to the child process using this customForkingPickler
:Note the "send information to the child" section. It's using the
dump
function, which usesForkingPickler
to pickle the data, which means your instance method can be pickled.Now, when you use methods on
multiprocessing.Pool
to send a method to a child process, it's using amultiprocessing.Pipe
to pickle the data. In Python 2.7,multiprocessing.Pipe
is implemented in C, and callspickle_dumps
directly, so it doesn't take advantage of theForkingPickler
. That means pickling the instance method doesn't work.However, if you use
copy_reg
to register theinstancemethod
type, rather than a customPickler
, all attempts at pickling will be affected. So you can use that to enable pickling instance methods, even viaPool
:Output:
Also note that in Python 3.x,
pickle
can pickle instance method types natively, so none of this stuff matters any more. :)