Многопроцессорный выход отличается от Linux и Windows - почему?

Я пытаюсь передать общий секрет дочерним процессам. В среде Linux это работает. В среде Windows ребенок не получает общий секрет. Три приведенных ниже файла - это простой пример того, что я пытаюсь сделать:

main.py

import multiprocessing
import module1
import module2

if __name__ == "__main__":
    module1.init()
    process = multiprocessing.Process(target=module2.start)
    process.start()
    process.join()

module1.py

import ctypes
import multiprocessing

x = None

def init():
    global x
    x = multiprocessing.Value(ctypes.c_wchar_p, "asdf")

module2.py

import module1

def start():
    print(module1.x.value)

В среде Ubuntu 14.04 на Python 3.5 я получаю следующий вывод:

$ python3 main.py
asdf

В среде CentOS 7 я получаю следующий вывод:

$ python3 main.py
asdf

Используя подсистему Windows для Linux в Windows 10 (как до, так и после обновления Creator, поэтому Ubuntu 14.04 и 16.04), я получаю следующий вывод:

$ python3 main.py
asdf

Однако, как в Windows 7, так и в Windows 10, используя 3.5 или 3.6, я получаю AttributeError вместо указанного выше:

Process Process-1:
Traceback (most recent call last):
  File "C:\Python\Python35\lib\multiprocessing\process.py", line 249, in _bootstrap
    self.run()
  File "C:\Python\Python35\lib\multiprocessing\process.py", line 93, in run
    self._target(*self._args, **self._kwargs)
  File "H:\Development\replicate-bug\module2.py", line 5, in start
    print(module1.x.value)
AttributeError: 'NoneType' object has no attribute 'value'

Я использую общий тип ctype. Это значение должно быть унаследовано дочерним процессом.

Почему я получаю этот AttributeError в среде Windows, но не в среде Linux?

Ответ 1

Как упоминалось в одном из сообщений автоматически связанных на боковой панели, окна не имеют системного кода fork, присутствующего в * системах NIX.

Это означает, что вместо совместного использования глобального состояния (как это делают процессы NIX) дочерний процесс Windows в основном полностью разделен. Это включает в себя модули.

Что происходит с подозрительным, так это то, что модуль загружается заново, а module1, который вы получаете внутри module2.start, не совсем тот модуль, который вы ожидали.

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

модуль 2

def start(mod1):
    print(mod1.x.value)

main.py

if __name__ == '__main__':
    module1.init()
    process = multiprocessing.Process(target=module2.start, args=(module1,))
    process.start()
    process.join()