I have several multiprocessing.Process
es and would like them to consume (queue get()
) callable non-picklable objects and call them. These were created before the fork()
, so they shouldn't need pickling.
Using multiprocessing.Queue
doesn't work as it tries to pickle everything:
import multiprocessing as mp
# create non-global callable to make it unpicklable
def make_callable():
def foo():
print("running foo")
return foo
def bar():
print("running bar")
def runall(q):
while True:
c = q.get()
if c is None:
break
c()
if __name__ == '__main__':
q = mp.Queue()
call = make_callable()
p = mp.Process(target=runall, args=(q,))
p.start()
q.put(bar)
q.put(call)
q.put(None)
p.join()
running bar
Traceback (most recent call last):
File "/usr/lib64/python3.7/multiprocessing/queues.py", line 236, in _feed
obj = _ForkingPickler.dumps(obj)
File "/usr/lib64/python3.7/multiprocessing/reduction.py", line 51, in dumps
cls(buf, protocol).dump(obj)
AttributeError: Can't pickle local object 'make_callable.<locals>.foo'
An implementation equivalent would be putting all objects into a global (or passed) list and passing just indexes, which works:
import multiprocessing as mp
# create non-global callable to make it unpicklable
def make_callable():
def foo():
print("running foo")
return foo
def bar():
print("running bar")
def runall(q, everything):
while True:
c = q.get()
if c is None:
break
everything[c]()
if __name__ == '__main__':
q = mp.Queue()
call = make_callable()
everything = [bar, call]
p = mp.Process(target=runall, args=(q,everything))
p.start()
q.put(0)
q.put(1)
q.put(None)
p.join()
running bar
running foo
The problem is that while I know that none of the callables passed will be garbage collected (and thus their addresses will stay valid), I do not have the full list beforehand.
I also know I could probably use multiprocessing.Manager
and its Queue
implementation using a Proxy
object, but this seems like a lot of overhead, especially as in the real implementation I would be passing other picklable data as well.
Is there a way to pickle and pass only the address reference to an object, shared across multiple processes?
Thanks!