Reputation: 7550
In the following program, when I append the process to a list (a seemingly pointless thing to do), it runs as expected. But if I remove the append, the processes destructor is called many times before it is even run. There are only n
constructions but (n)(n+1)/2
(where n
is the number of processes) destructions. This leads me to believe that each process is being copied into each new process and then immediately destroyed. Perhaps that is how the multiprocessing module works. It makes sense, since each process is a fork of the current one. But, what is the significance of appending to the list? Why does simply doing that stop this behaviour?
Here's the test and sample output:
import multiprocessing
class _ProcSTOP:
pass
class Proc(multiprocessing.Process):
def __init__(self, q, s):
s.i += 1
self._i = s.i
self._q = q
print('constructing:', s.i)
super().__init__()
def run(self):
dat = self._q.get()
while not dat is _ProcSTOP:
self._q.task_done()
dat = self._q.get()
self._q.task_done()
print('returning: ', self._i)
def __del__(self):
print('destroying: ', self._i)
if __name__ == '__main__':
q = multiprocessing.JoinableQueue()
s = multiprocessing.Manager().Namespace()
s.i = 0
pool = []
for i in range(4):
p = Proc(q, s)
p.start()
pool.append(p) # This is the line to comment
for i in range(10000):
q.put(i)
q.join()
for i in range(4):
q.put(_ProcSTOP)
q.join()
print('== complete')
Sample output with append:
constructing: 1
constructing: 2
constructing: 3
constructing: 4
returning: 3
returning: 2
== complete
returning: 1
returning: 4
destroying: 4
destroying: 3
destroying: 2
destroying: 1
Sample output without append:
constructing: 1
constructing: 2
constructing: 3
destroying: 1
constructing: 4
destroying: 1
destroying: 2
destroying: 3
destroying: 1
destroying: 2
returning: 1
returning: 4
returning: 2
== complete
returning: 3
destroying: 1
destroying: 3
destroying: 2
destroying: 4
Upvotes: 8
Views: 273
Reputation: 1052
Adding the object to a list will prevent it from being deleted in child processes because after forking it will call "os._exit()" instead of clearing the whole stack
The relevant code is in multiprocessing/forking.py (constructor of Popen, which is called on "p.start()")
self.pid = os.fork()
if self.pid == 0:
if 'random' in sys.modules:
import random
random.seed()
code = process_obj._bootstrap()
sys.stdout.flush()
sys.stderr.flush()
os._exit(code)
Where _bootstrap setups the new process and calls "run" (code is in multiprocessing/process.py)
Upvotes: 1