какой контент будет скопирован в подпроцесс из родительских процессов при создании нового подпроцесса в python с многопроцессорными модулями

1

В некоторых статьях мне рассказывалось, что при создании нового подпроцесса операционная система почти скопирует все данные из partent, включая структуру процессов, стек, кучу и так далее. Итак, я думаю, что глобальные переменные, статические переменные могут быть скопированы в подпроцесс, чьи contens равны родительским значениям при моменте вызова fork(). Но после результата кода python меня путают:

from multiprocessing import Process
ids = []
ids.extend([1, 2, 3, 4])

def worker(sub_id):
    global ids
    print("sub_id=%s, the content of ids: [%s]" % (sub_id, ",".join(["%s" % x for x in ids])))

def init():
    global ids
    ids.append(-100)

def main():
    init()
    sub_process = list()
    for i in range(2):
        process = Process(target=worker, args=(i, ))
        process.start()
        sub_process.append(process)
    for p in sub_process:
        p.join()
    global ids
    ids.append(100)
    print("the main process, the content of ids: [%s]" % (",".join(["%s" % x for x in ids])))

if __name__ == "__main__":
    main()

результаты выполнения вышеуказанных кодов:

sub_id=0, the content of ids: [1,2,3,4]
sub_id=1, the content of ids: [1,2,3,4]
the main process, the content of ids: [1,2,3,4,-100,100]

Ожидаемые результаты:

sub_id=0, the content of ids: [1,2,3,4, -100]
sub_id=1, the content of ids: [1,2,3,4, -100]
the main process, the content of ids: [1,2,3,4,-100,100]

Я не знаю, почему изменения ids в функции init() не копируются в ids.extend([1, 2, 3, 4]), но изменения в глобальном разделе ids.extend([1, 2, 3, 4]) видны для ids.extend([1, 2, 3, 4]).

спасибо за каждый ответ.

  • 1
    На какой ты платформе? А какая версия Python?
  • 0
    Я подозреваю, что вы находитесь в Windows, где нет fork , поэтому по умолчанию используется метод запуска "spawn" . Для этого нужно запустить совершенно новый интерпретатор Python и импортировать ваш модуль - это означает, что код верхнего уровня, подобный этому, ids = [] и ids.extend(…) запускается в дочернем процессе (но код, защищенный этим __main__ Guard, не Get Run), так что вы получите равный список, но на самом деле он не скопирован из родительского, а просто создан таким же образом.
Показать ещё 1 комментарий
Теги:
python-multiprocessing

1 ответ

1

Как объясняется в документах, multiprocessing имеет три разных способа запуска процессов. Двумя основными являются fork и spawn. 1

  • fork копирует ваш родительский процесс. Вы были правы на носу о том, что это делает: 2 дети начинают с копий родительских глобалов и т.д.

  • spawn создает совершенно новый процесс, запускает интерпретатор Python и import ваш модуль.

В Unix fork является значением по умолчанию, но в качестве параметров доступны spawn и forkserver. В Windows, spawn является значением по умолчанию и единственным вариантом, поскольку Windows не предоставляет API fork.


Поскольку вы работаете в Windows, дети не получают копию ids от родителя, но код ids = [] и ids.extend(…) запускается при import, поэтому все они имеют одинаковые значения. Но любой код, защищенный __main__ guard, не запускается при import, поэтому они не вызывают main, поэтому нет init, поэтому no ids.append(-100).


multiprocessing библиотека разработана таким образом, что вы можете использовать ее таким образом, чтобы она работала одинаково на всех платформах. 3 Большинство деталей подробно описаны в разделе "Руководства по программированию " в документах, но основная идея заключается в следующем: не предполагайте, что глобальные скопированные копии скопированы или они не скопированы.

Это означает, что у вас вообще не будет никакого кода вне защитника __main__ на верхнем уровне, за исключением операторов import, def и class и, возможно, нескольких простых глобальных постоянных присвоений.

Любая сложная настройка, которую вам нужно сделать, вы делаете в каждом дочернем процессе. 4 Все, что вы хотите разделить между процессами (например, Lock или Queue), вы создаете внутри __main__ guard и передаете детям в качестве аргументов.


1. Для forkserver см. Документацию для подробностей;это в основном там, где есть программы, которые используют библиотеки, которые ожидают делать причудливые вещи с потоками, которые не очень хорошо работают с fork, что является обычным явлением в macOS, хотя это может быть полезно и в некоторых других случаях.

2. Есть некоторые сложности в отношении таких вещей, как открытые файлы, но они здесь не важны.

3. Вы можете просто указать spawn а затем обработать каждую платформу, такую как Windows.Но на некоторых платформах Unix возможно медленное spawn процессов.Кроме того, получение таких вещей, как совлокальные файлы для работы, может быть болезненным в Unix, поэтому иногда проще писать код, который работает с fork или spawn, чем писать код, который работает с spawn на Unix или spawn в Windows.Однако использование forkserver в Unix и spawn на Windows может, однако, быть хорошим компромиссом /

4. Если вы используете Pool s, используйте функцию initializer.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню