При использовании многопроцессорной обработки на разных платформах способ обработки sys.modules
кажется другим. Если вы измените sys.modules
dict в Linux и создадите дочерний процесс, этот процесс, похоже, наследует dict, но в Macos для sys.modules
создается новый объект. Это потому, что способ работы многопроцессорности не зависит от ОС?
Это связано с тем, что Linux использует fork()
при создании дочернего процесса, а Macos делает это по-другому?
Например, если я попробую этот фрагмент:
import multiprocessing
import sys
import time
from types import ModuleType
def worker():
# print the id of the dict
print('worker', id(sys.modules), 'foo' in sys.modules)
def main():
# modify the sys.modules dict
sys.modules['foo'] = ModuleType('foo')
# print the id of the dict
print('main', id(sys.modules), 'foo' in sys.modules)
p = multiprocessing.Process(target=worker, daemon=True)
p.start()
time.sleep(0.1)
if __name__ == '__main__':
main()
Я получаю следующие результаты:
Linux (x86_64, питон 3.10.1)
main 139897509461248 True
worker 139897509461248 True
MacOS (M1 ARM, питон 3.10.1)
main 4307217856 True
worker 4334595520 False
Для контекста: я пытаюсь создать очередь задач, используя только stdlib (аля Celery). Тем не менее, я не смог найти идиоматический способ передать маринованные функции рабочим процессам, но в большинстве случаев я получаю, что поиск атрибута завершается сбоем при распаковке сериализованных функций (при сериализации вызываемых объектов pickle просто сохраняет имя и модуль, и это загружается во время выполнения при десериализации).
В качестве обходного пути я пытаюсь изменить атрибут функции __module__
при сериализации функции и добавить ее в динамически созданный модуль. Кажется, это исправляет ошибку поиска атрибутов, но похоже на плохой хак.
В MacOS новые многопроцессорные процессы создаются, а не разветвляются по умолчанию. Это менее эффективно и включает повторный импорт пакетов и модулей.
Из документы:
Changed in version 3.8: On macOS, the spawn start method is now the default. The fork start method should be considered unsafe as it can lead to crashes of the subprocess. See bpo-33725.
Вы можете попробовать multiprocessing.set_start_method()
на свой страх и риск.