質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

840閲覧

Multiprosessingを用いるとNameErrorになる

namuyan

総合スコア76

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

1クリップ

投稿2018/03/09 12:08

編集2018/03/09 12:50

Windows10 64bit, Python3.5 64bit
dillでバイナリ化したオブジェクトをMultiprocessingで実行しようとするとNameErrorになります。

Python

1import dill 2from multiprocessing import Process 3from threading import Thread 4 5 6def wei(i): 7 print("wei", i) 8 i += 3 9 return i 10 11 12def test(i): 13 while i < 10: 14 i += 1 15 i = wei(i) 16 print("end", i) 17 18 19def work(code): 20 print(wei(4)) 21 obj = dill.loads(code) 22 obj(1) 23 24 25def exe(): 26 code= dill.dumps(test) 27 print(code) 28 Process(target=work, args=(code,)).start() 29 30if __name__ == '__main__': 31 exe()

例として上記のコードを実行するとprint(wei(4))まではエラー無く実行できます。
しかし、obj(1)に入るとNameError: name 'wei' is not definedとなってしまいます。

dillで生成したObjectのみNameErrorになります。ProcessをThreadにするとエラーが出ないことからMultiprocessingとdillの組み合わせで何か悪い事が起きていると考えています。
しかし、何が原因かよくわからず困っています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/03/09 12:40

OS, 実行方法などの情報が必要かもしれません。mac python 3.6.4上記のコード問題なく動きましt。
namuyan

2018/03/09 12:50

つい忘れていました、追記に明記しました。
guest

回答2

0

ベストアンサー

WindowsのマルチプロセスはLinuxのそれとは事情が違う、というのだけは知っているのですが、詳しいことは知りません。エラーコードを見る限り、サブプロセス上でtestを実行したときに、weiがスコープから外れてしまって実行できなくなっているようですね。マルチスレッドでは同一のプロセスなのでweitestのスコープに残るため、問題なく動くんだと思います。

実行するweiが見つからなければ引数で渡してやれば・・・というアイディアで書いたのが下のコードです。たぶんWindows環境でも動くと思います。

python

1import dill 2from multiprocessing import Process 3from threading import Thread 4 5def wei(i): 6 print("wei", i) 7 i += 3 8 return i 9 10def test(i, wei): 11 while i < 10: 12 i += 1 13 i = wei(i) 14 print("end", i) 15 16def work(code, wei): 17 print(wei(4)) 18 obj = dill.loads(code) 19 obj(1, wei) 20 21def exe(): 22 code= dill.dumps(test) 23 print(code) 24 Process(target=work, args=(code, wei)).start() 25 26if __name__ == '__main__': 27 exe()

投稿2018/03/09 16:46

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

namuyan

2018/03/10 06:23

確かに動きますね。デシリアライズしたオブジェクトを実行するのと何が違うのか.... 引数をglobalsにしてグローバル関数に上書きすると使えるようになりますが、デシリアライズする時にグローバル関数もまた生成されるのかもしれません?Macで動くのが謎です。
guest

0

途中までですけど・・・

Processの動作のデフォルトは以下ですが、

Windows: spawn
Linux: fork

Linux(Macintosh)では動きWindowsでは動かないということなので上記動作の違いが影響していると思います。以下を実行してみますと

Python

1import dill 2 3def foo(): 4 print('foo') 5 bar() # line 7 6 7def bar(): 8 print('bar') 9 10if __name__ == '__main__': 11 code = dill.dumps(foo) 12 bar = None 13 o = dill.loads(code) 14 foo()

File "C:/Users/keiji/PyCharmProjects/tkinter_test/spawn_test.py", line 7, in foo
bar()
TypeError: 'NoneType' object is not callable

となることより、dumpsでは指定したオブジェクト(test関数オブジェクト)のみしかシリアライズされていないことがわかります。それは自然な仕様と考えられます。
そうなのだとしたら別プロセスへ環境を復元するには必要なオブジェクトを全てシリアライズ、デシリアライズする必要があると考えなければならないのではないでしょうか?

Windowsでspawnした場合でも実行に必要な関数群自体は新たに起動されたインタープリタへ復元されるので動くような気もしますが、「同一のインタープリタではない」のでid(obj)などオブジェクトの内部情報は元の環境とは異なると思います、spawn/forkで振る舞いに違いがでるのはそういった要因なのかも知れないと思いました。

(ただdillが何を保証しているか何もドキュメントを読んでないので、上記は単なる推測の域を出ません)


追記:書き忘れたことあるので追記しておきます

グローバル変数のスナップショットを取るのがdillの典型的な使い方だと思いますが、質問者さんがわざわざ関数のシリアライズをしているのはdillがどんな動きをするか調べているということなのかなと思いました。

子供プロセスを起動して親プロセスで定義されている関数群を用いるだけならspawnモード/forkモードにかかわらずProcessで起動した子供プロセスでそのまま親プロセスのモジュールの状態が再現された状態で動きます(※1)。つまり特殊なことをしようとしているのでない限りdillにより関数のシリアライズ/デシリアライズの必要はないと思います。

実際、Processを用いても下記のようにfoo/bar/baz全て定義された状態で子プロセスが動きます。(質問者さんもご存知だとは思います)

Python

1from multiprocessing import Process 2 3def foo(): 4 print('foo') 5 bar() 6 7def bar(): 8 print('bar') 9 baz() 10 11def baz(): 12 print('baz') 13 14if __name__ == '__main__': 15 p = Process(target=foo, args=()) 16 p.start() 17 print('waiting for child') 18 p.join() 19 print('child has been finished')

※1: forkシステムコールがないWindowsでは親プロセスとほぼ同一の環境で子供プロセスが開始するLinuxとは若干ことなるセマンティクスになります。Processのリファレンスにある通りです。

投稿2018/03/09 15:36

編集2018/03/09 18:18
KSwordOfHaste

総合スコア18392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問