Многопроцессорность Python: объект передается по значению?

Я пробовал следующее:

from multiprocessing import Pool

def f(some_list):
    some_list.append(4)
    print 'Child process: new list = ' + str(some_list)
    return True

if __name__ == '__main__':

    my_list = [1, 2, 3]
    pool = Pool(processes=4)
    result = pool.apply_async(f, [my_list])
    result.get()

    print 'Parent process: new list = ' + str(my_list)

Что я получаю:

Child process: new list = [1, 2, 3, 4]
Parent process: new list = [1, 2, 3]

Таким образом, это означает, что my_list был передан по значению, поскольку он не изменялся. Итак, существует ли правило, что оно действительно передается по значению при передаче другому процессу? Спасибо.


person jazzblue    schedule 09.01.2015    source источник


Ответы (2)


Как сказал Андре Ласло, библиотека multiprocessing должна обрабатывать все объекты, переданные методам multiprocessing.Pool, чтобы передать их рабочим процессам. В результате процесса травления в рабочем процессе создается отдельный объект, поэтому изменения, внесенные в объект в рабочем процессе, не влияют на объект в родительском. В Linux объекты иногда передаются дочернему процессу через наследование fork (например, multiprocessing.Process(target=func, args=(my_list,))), но в этом случае вы получаете версию объекта с возможностью копирования при записи в дочернем процессе, поэтому вы все равно получаете отдельные копии. когда вы пытаетесь изменить его в любом процессе.

Если вы хотите совместно использовать объект между процессами, вы можете использовать multiprocessing.Manager для этого:

from multiprocessing import Pool, Manager

def f(some_list):
    some_list.append(4)
    print 'Child process: new list = ' + str(some_list)
    return True

if __name__ == '__main__':

    my_list = [1, 2, 3]
    m = Manager()
    my_shared_list = m.list(my_list)
    pool = Pool(processes=4)
    result = pool.apply_async(f, [my_shared_list])
    result.get()

    print 'Parent process: new list = ' + str(my_shared_list)

Выход:

Child process: new list = [1, 2, 3, 4]
Parent process: new list = [1, 2, 3, 4]
person dano    schedule 09.01.2015
comment
Спасибо. Что, если я просто заставлю свою функцию f возвращать измененный объект? Сильно ли я потеряю в эффективности по сравнению с использованием Manager? - person jazzblue; 09.01.2015
comment
@jazzblue Если ваш вариант использования позволяет вам просто изменить список в дочернем элементе и вернуть его родителю, используйте его вместо Manager. С объектом Manager каждый раз, когда вы обращаетесь к общему списку, вы фактически выполняете вызов IPC к процессу Manager. Это намного медленнее, чем исходный доступ к списку. Возвращая объект родителю, вы принимаете однократный вызов IPC передачи объекта списка обратно между процессами, но если вы не имеете дело с огромным списком, это, вероятно, не произойдет. перевешивают стоимость всех IPC, необходимых для общего списка. - person dano; 09.01.2015

Библиотека multiprocessing сериализует объекты, используя pickle для передачи их между процессами.

Это обеспечивает безопасное взаимодействие между процессами, и два процесса могут использовать «одинаковые» объекты без использования общей памяти.

person André Laszlo    schedule 09.01.2015
comment
Вы должны заменить каждый экземпляр потока выше на process. - person dano; 09.01.2015