Python multiprocessing.Pool и выбор аргументов - PullRequest
0 голосов
/ 24 сентября 2018

Рассмотрим следующий пример:

import multiprocessing as mp

def job(l):
    l.append(1)
    return l

if __name__ == "__main__":
    pool = mp.Pool(1)
    my_list = []
    out = pool.map(job, [my_list for i in range(5)])
    pool.close()
    pool.join()
    print(out)

При вызове pool.map я ожидаю, что аргументы будут выбраны, а затем не выбраны после вызова задания (таким образом, воссоздаются каждый раз).Однако наблюдаемый результат составляет

[[1, 1], [1, 1], [1, 1], [1, 1], [1]]

Может кто-нибудь объяснить, что происходит?Я ожидал, что вывод будет список из пяти [1] или [[1], [1, 1], ..., [1, 1, 1, 1, 1]], ни один из которых не имеет место.

Ответы [ 2 ]

0 голосов
/ 25 сентября 2018

Параметр chunksize для pool.map является причиной вашей путаницы.Очевидно, он выберет автоматическую настройку chunksize = 2 для вашей настройки, потому что вы получите результат, который вы наблюдали, также с явной установкой chunksize=2.

С chunksize=1 вы получите [[1], [1], [1], [1], [1]], а с chunksize=3 вы получите [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1], [1, 1]].

Если вы расширите свой код распечатками, вы сможете наблюдать за тем, что происходит:

import multiprocessing as mp

def job(l):
    print(f'before append {l}')
    l.append(1)
    print(f'after append {l}')
    return l

if __name__ == "__main__":
    pool = mp.Pool(1)
    my_list = []
    out = pool.map(job, [my_list for _ in range(5)], chunksize=2)
    pool.close()
    pool.join()
    print(out)

Это даст вам такой вывод:

before append []
after append [1]
before append [1]
after append [1, 1]
before append []
after append [1]
before append [1]
after append [1, 1]
before append []
after append [1]
[[1, 1], [1, 1], [1, 1], [1, 1], [1]]

Process finished with exit code 0

Вы можете видеть, что «перед добавлением» начинается только три раза с пустым списком, а не пять раз, как вы ожидаете.Это потому, что с chunksize=2 и пятью элементами в итерируемом у вас есть 5/2 = 2,5 задачи.Половина задачи невозможна, поэтому у вас есть 3 задачи: 2 задачи с кусочками из двух элементов и одна задача с кусочком из одного элемента.

Теперь для первых двух задач перваявыполнение вашей функции job получает пустой список без добавлений и добавляет 1.Затем второе выполнение получает тот же список, что и первое изменение, только что измененное, потому что ваши элементы являются просто ссылками на тот же список в этой задаче.Второе выполнение также изменяет результат первого выполнения, поскольку оба изменяют один и тот же базовый объект.После второго выполнения задача завершается, и результат двух выполнений [[1, 1], [1, 1]] возвращается родителю.Это происходит, как мы уже говорили, для первых двух задач.

Третья задача имеет только одно выполнение job, и ее результат не модифицируется второй, поэтому результат только [1].

Если вы добавите for obj in out: print(id(obj)) в конце своего кода, вы увидите, что в результате вы получите три разных идентификатора для трех отдельных списков, столько же задач было создано для обработки вашего итерируемого (CPython).:

140584841382600
140584841382600
140584841383432
140584841383432
140584841383368
0 голосов
/ 25 сентября 2018

Это дает разные результаты с разным числом процессов, что означает, что вы делаете что-то, что не безопасно для процесса;в этом случае работа с собственным списком в (потенциально) нескольких процессах.

Я не совсем понимаю, чего вы пытаетесь достичь, но это, по крайней мере, ведет себя последовательно:

from multiprocessing import Pool, Manager


def job(l):
    l.append(1)
    return l


if __name__ == "__main__":
    manager = Manager()

    for proc_count in range(1, 6):
        print(proc_count)
        pool = Pool(proc_count)
        my_list = manager.list()
        out = pool.map(job, [my_list for i in range(5)])
        pool.close()
        pool.join()
        print(list(list(o) for o in out))

Если это не то, к чему вы стремитесь, забыв менеджер, сброс my_list и использование [list() for i in range(5)] также приводит к последовательному, хотя и другому, поведению.

...