Добавление процесса в список (но ничего с ним не делая) изменяет поведение программы

#python #python-3.x #subprocess #multiprocessing

#python #python-3.x #подпроцесс #многопроцессорность

Вопрос:

В следующей программе, когда я добавляю процесс в список (что, казалось бы, бессмысленно), он выполняется так, как ожидалось. Но если я удаляю добавление, деструктор процессов вызывается много раз, прежде чем он будет запущен. Есть только n конструкции, но (n)(n 1)/2 (где n — количество процессов) разрушения. Это наводит меня на мысль, что каждый процесс копируется в каждый новый процесс, а затем немедленно уничтожается. Возможно, именно так работает модуль многопроцессорной обработки. Это имеет смысл, поскольку каждый процесс является ответвлением текущего. Но в чем смысл добавления в список? Почему простое выполнение этого останавливает это поведение?

Вот тест и пример вывода:

 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')
  

Пример вывода с добавлением:

 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
  

Пример вывода без добавления:

 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
  

Комментарии:

1. Как вы думаете, это как-то связано со сборкой мусора? Симптомы указывают на то, что необходимо сохранить ссылку на экземпляр класса.

2. @Dave. Это хороший момент. Я подозреваю, что вы на правильном пути.

Ответ №1:

Добавление объекта в список предотвратит его удаление в дочерних процессах, потому что после разветвления он вызовет «os._exit()» вместо очистки всего стека

Соответствующий код находится в multiprocessing/forking.py (конструктор Popen, который вызывается в «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)
  

Где _bootstrap настраивает новый процесс и вызывает «run» (код находится в multiprocessing/process.py )