Зомби государственная многопроцессорная библиотека python3 - PullRequest
0 голосов
/ 13 ноября 2018

Мой вопрос касается замены функции join (), чтобы избежать несуществующего или зомби-состояния уже завершенных процессов при использовании многопроцессорной библиотеки python3. Есть ли альтернатива, которая может приостановить дочерние процессы от завершения, пока они не получат зеленый свет от основного процесса? Это позволяет им правильно завершить работу, не переходя в состояние зомби?

Я подготовил быструю иллюстрацию, используя следующий код, который запускает 20 различных процессов, первый процесс занимает 10 секунд работы под нагрузкой, а все остальные - 3 секунды работы:

import os
import sys
import time
import multiprocessing as mp
from multiprocessing import Process

def exe(i):
    print(i)    
    if i == 1:
        time.sleep(10)
    else:
        time.sleep(3)
procs = []
for i in range(1,20):
    proc = Process(target=exe, args=(i,))
    proc.start()
    procs.append(proc)

for proc in procs:
    print(proc) # <-- I'm blocked to join others till the first process finishes its work load
    proc.join()

print("finished")

Если вы запустите скрипт, вы увидите, что все остальные процессы переходят в состояние зомби до тех пор, пока функция join () не выйдет из первого процесса. Это может сделать систему нестабильной или перегруженной!

Спасибо

1 Ответ

0 голосов
/ 13 ноября 2018

За эту тему , Марко Раухамаа пишет:

Если вам не важно знать, когда завершаются дочерние процессы, вы можете просто проигнорировать сигнал SIGCHLD:

import signal
signal.signal(signal.SIGCHLD, signal.SIG_IGN)

Это предотвратит появление зомби.

Справочная страница wait(2) объясняет:

POSIX.1-2001 указывает, что если для расположения SIGCHLD установлено значение SIG_IGN или флаг SA_NOCLDWAIT установлен для SIGCHLD (см. sigaction (2)), то дети, которые заканчиваются, не становятся зомби и вызов wait () или waitpid () будет блокироваться, пока все дети не завершается, а затем завершается ошибкой с errno, установленным в ECHILD. (Оригинал Стандарт POSIX оставил поведение установки SIGCHLD в SIG_IGN неопределенные. Обратите внимание, что хотя по умолчанию расположение SIGCHLD "игнорировать", явно устанавливая расположение к SIG_IGN приводит к различному лечению детей с зомби-процессом.)

Linux 2.6 соответствует требованиям POSIX. Тем не менее, Linux 2.4 (и ранее) нет: если вызовы wait () или waitpid () выполняются во время SIGCHLD игнорируется, вызов ведет себя так же, как если бы SIGCHLD не игнорировались, то есть блокировались вызовы до следующего потомка завершается, а затем возвращает идентификатор процесса и статус этого потомка.

Таким образом, если вы используете Linux 2.6 или POSIX-совместимую ОС, использование приведенного выше кода позволит дочерним процессам завершаться, не становясь зомби. Если вы не используете POSIX-совместимую ОС, то в приведенной выше ветке предлагается ряд опций. Ниже приведен один вариант, несколько похожий на третье предложение Марко Раухамаа .


Если по какой-то причине вам нужно знать, когда выходят дочерние процессы, и хотите обрабатывать (по крайней мере, некоторые из них) по-другому, тогда вы можете настроить очередь позволяют дочерним процессам сигнализировать о главном процессе, когда они завершены. затем основной процесс может вызвать соответствующее соединение в том порядке, в котором он получает элементы из очереди:

import time
import multiprocessing as mp

def exe(i, q):
    try:
        print(i)    
        if i == 1:
            time.sleep(10)
        elif i == 10:
            raise Exception('I quit')
        else:
            time.sleep(3)
    finally:
        q.put(mp.current_process().name)

if __name__ == '__main__':
    procs = dict()
    q = mp.Queue()
    for i in range(1,20):
        proc = mp.Process(target=exe, args=(i, q))
        proc.start()
        procs[proc.name] = proc

    while procs:
        name = q.get()
        proc = procs[name]
        print(proc) 
        proc.join()
        del procs[name]

    print("finished")

дает результат, подобный

...    
<Process(Process-10, stopped[1])>  # <-- process with exception still gets joined
19
<Process(Process-2, started)>
<Process(Process-4, stopped)>
<Process(Process-6, started)>
<Process(Process-5, stopped)>
<Process(Process-3, stopped)>
<Process(Process-9, started)>
<Process(Process-7, stopped)>
<Process(Process-8, started)>
<Process(Process-13, started)>
<Process(Process-12, stopped)>
<Process(Process-11, stopped)>
<Process(Process-16, started)>
<Process(Process-15, stopped)>
<Process(Process-17, stopped)>
<Process(Process-14, stopped)>
<Process(Process-18, started)>
<Process(Process-19, stopped)>
<Process(Process-1, started)>      # <-- Process-1 ends last
finished
...