グローバル変数を利用する
まずは、Python3.7までのデフォルトサブプロセス開始方法だった forkを指定して 、グローバル変数を利用しているスクリプトを実行してみます。
$ python3.8 use_global.py fork
INFO:__main__:set_start_method: fork
INFO:__main__:plugin loaded.
INFO:__main__:['func0: 0', 'func1: 1', 'func0: 2', 'func1: 3', 'func0: 4', 'func1: 5', 'func0: 6', 'func1: 7', 'func0: 8', 'func1: 9']
次に、サブプロセス開始方法をデフォルトのspawnのまま、同じスクリプトを実行してみます。
$ python3.8 use_global.py
INFO:__main__:plugin loaded.
multiprocessing.pool.RemoteTraceback:
"""
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/pool.py", line 125, in worker
result = (True, func(*args, **kwds))
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/pool.py", line 48, in mapstar
return list(map(*args))
File "/Users/makoto/projects/chore_multiprocessing_py38/use_global.py", line 29, in process
return plugin_registry.process(remainder, i)
AttributeError: 'NoneType' object has no attribute 'process'
"""
macOSのPython3.8からデフォルトになった spawn はサブプロセスの起動に新しいインタプリタを起動します。 グローバル変数は引き継がれず、plugin_registryグローバル変数はNoneのままになっており、 AttributeError が発生します。
グローバル変数ではなく引数で渡す
新しく起動したインタプリタに対して、対象の関数と引数のタプルがpickleされたものが渡されるので、グローバル変数にしておく代わりに 引数として渡して あげれば動作します。
$ python3.8 use_args.py
INFO:__main__:plugin loaded.
INFO:__main__:['func0: 0', 'func1: 1', 'func0: 2', 'func1: 3', 'func0: 4', 'func1: 5', 'func0: 6', 'func1: 7', 'func0: 8', 'func1: 9']
インタプリタの起動について、pdbでコマンドっぽい変数の中身を確認してみたところ、次のような状態でした。
> /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/multiprocessing/popen_spawn_posix.py(58)_launch()
cmd = spawn.get_command_line(tracker_fd=tracker_fd,
pipe_handle=child_r)
(Pdb) cmd
['/Library/Frameworks/Python.framework/Versions/3.8/bin/python3.8', '-c', 'from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=8, pipe_handle=19)', '--multiprocessing-fork']