Подтвердить что ты не робот

Когда нужно позвонить .join() в процессе?

Я читаю различные руководства по модулю многопроцессорности в Python, и мне трудно понять, почему/когда вызывать process.join(). Например, я наткнулся на этот пример:

nums = range(100000)
nprocs = 4

def worker(nums, out_q):
    """ The worker function, invoked in a process. 'nums' is a
        list of numbers to factor. The results are placed in
        a dictionary that pushed to a queue.
    """
    outdict = {}
    for n in nums:
        outdict[n] = factorize_naive(n)
    out_q.put(outdict)

# Each process will get 'chunksize' nums and a queue to put his out
# dict into
out_q = Queue()
chunksize = int(math.ceil(len(nums) / float(nprocs)))
procs = []

for i in range(nprocs):
    p = multiprocessing.Process(
            target=worker,
            args=(nums[chunksize * i:chunksize * (i + 1)],
                  out_q))
    procs.append(p)
    p.start()

# Collect all results into a single result dict. We know how many dicts
# with results to expect.
resultdict = {}
for i in range(nprocs):
    resultdict.update(out_q.get())

# Wait for all worker processes to finish
for p in procs:
    p.join()

print resultdict

Из того, что я понимаю, process.join() блокирует вызывающий процесс до тех пор, пока процесс, метод которого был вызван, завершил выполнение. Я также считаю, что дочерние процессы, которые были запущены в приведенном выше примере кода, завершают выполнение после завершения целевой функции, то есть после того, как они подтолкнули их результаты к out_q. Наконец, я считаю, что out_q.get() блокирует вызывающий процесс, пока не будут получены результаты. Таким образом, если вы считаете код:

resultdict = {}
for i in range(nprocs):
    resultdict.update(out_q.get())

# Wait for all worker processes to finish
for p in procs:
    p.join()

основной процесс блокируется вызовами out_q.get() до тех пор, пока каждый отдельный рабочий процесс не завершит вывод своих результатов в очередь. Таким образом, к тому моменту, когда основной процесс выходит из цикла for, каждый дочерний процесс должен завершить выполнение, правильно?

Если это так, есть ли причина для вызова методов p.join() в этот момент? Разве не все рабочие процессы уже завершены, и как это заставляет основной процесс "ждать завершения всех рабочих процессов"? Я прошу в основном, потому что я видел это в нескольких разных примерах, и мне любопытно, если я что-то не понял.

4b9b3361

Ответ 1

Попробуйте запустить это:

import math
import time
from multiprocessing import Queue
import multiprocessing

def factorize_naive(n):
    factors = []
    for div in range(2, int(n**.5)+1):
        while not n % div:
            factors.append(div)
            n //= div
    if n != 1:
        factors.append(n)
    return factors

nums = range(100000)
nprocs = 4

def worker(nums, out_q):
    """ The worker function, invoked in a process. 'nums' is a
        list of numbers to factor. The results are placed in
        a dictionary that pushed to a queue.
    """
    outdict = {}
    for n in nums:
        outdict[n] = factorize_naive(n)
    out_q.put(outdict)

# Each process will get 'chunksize' nums and a queue to put his out
# dict into
out_q = Queue()
chunksize = int(math.ceil(len(nums) / float(nprocs)))
procs = []

for i in range(nprocs):
    p = multiprocessing.Process(
            target=worker,
            args=(nums[chunksize * i:chunksize * (i + 1)],
                  out_q))
    procs.append(p)
    p.start()

# Collect all results into a single result dict. We know how many dicts
# with results to expect.
resultdict = {}
for i in range(nprocs):
    resultdict.update(out_q.get())

time.sleep(5)

# Wait for all worker processes to finish
for p in procs:
    p.join()

print resultdict

time.sleep(15)

И откройте диспетчер задач. Вы должны уметь видеть, что 4 подпроцесса находятся в состоянии зомби в течение нескольких секунд, прежде чем они будут завершены ОС (из-за вызовов соединения):

enter image description here

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

Ответ 2

В момент, когда вы вызываете join, все работники помещают свои результаты в свои очереди, но они не обязательно возвращаются, и их процессы могут еще не завершиться. Они могут или не могли этого сделать, в зависимости от времени.

Вызов join гарантирует, что всем процессам будет предоставлено время для правильного завершения.