サーバレス練習帳

着眼大局着手小局

【python】マルチスレッド(threading)の注意点

blog.bonprosoft.com

Pythonにはグローバルインタプリタロックと呼ばれるロック機構が備わっており、Pythonのスレッドがオブジェクトについて操作する前には、必ずGILを獲得する(ロックを行い、アクセス権を獲得する)必要があります。
つまり究極的にはPythonにおけるスレッドは、GILを獲得しなければ何も作業することができません。
公式ドキュメントにも記述されていますが、たとえマルチスレッドになっていたとしても、実際には並行処理を疑似的に実現するために、インタプリタが交互にロックを解放・獲得しているにすぎないのです。

PythonのあるスレッドからC++などで記述した外部のライブラリを呼び出している間、(C++側でPythonのスレッドを作成したりしない限りは)C++側のコードにはPythonのスレッド状態が割り当てられます。
C++側で処理を行っている際は、GILの制御タイミングはプログラマーが手動でタイミングを指定する必要があります。
つまり、最初の例で元のスレッドが全く実行されなかったのは、別のスレッドでGILがロックされたまま(全く解放のタイミングがなかったため)でした。

これ、きちんと考えないとマズイ感じがしてきた。

とりあえず、マルチプロセスにしてみるか?
wxPythonで作られたタスクトレイ常駐アイコンをマルチスレッドで動かしていたのだが、それをマルチプロセスに変えてみた。
文章は、ほぼそのままで楽ですね。

		# マルチスレッド方式
		'''
		self._thread = threading.Thread(target=self._taskTray.MainLoop, args=())
		self._thread.setDaemon(True)
		self._thread.start()
		'''
		# マルチプロセス方式
		self._process = multiprocessing.Process(target=self._taskTray.MainLoop, args=(),daemon=True)
		self._process.start()

ただ、動かしてみると、、、can't pickleとか出てきて、どうやらマルチプロセスの上でwxPythonは動かせなそうな感じです。

f:id:urbanplanner:20181125211010p:plain

仕方がない、マルチスレッドに戻るか。
しかし、実際問題、タスクトレイ常駐アイコンの反応が悪い。
スレッドにせずにタスクトレイ常駐アイコンを立ち上げた場合は反応が良いので、やはりこのやりかたには問題があるのだろうな。

subprocessを使って非同期にタスクトレイ常駐アイコンを立ち上げて、windowhandleの消滅をもってメインプロセスも消滅させるプログラムにするか?


hajimete-program.com

上記を読んで、subprocessを使って次のように実現できることを確認しました。
リサイズとか移動もできるんだ! すげー!けど、必要なのはpollによる生存監視のみ。

import subprocess
import time

child_process = subprocess.Popen(r'.\xxxxxxxx.exe')
poll = 1
while( poll != 0):	
	if(child_process.poll() == 0):
		poll = 0
	else:
		poll = 1	
	print(poll)
	time.sleep(1)

child_process.pollは生きている間はNone、終了すると0を返すようです。