クーの自由研究

マスターのかえるのクーは、弟子達の召喚術により新たな依り代を得てⅡ世として復活しました。

pythonのマルチプロセスを勉強しよう

一本道のプログラムは寄り道すると遅いので

にっこにっこに~。かえるのクーの助手の「井戸中 聖」です。

f:id:AssistantOfKoo:20200705225550j:plain

pythonのマルチプロセスを勉強します。OSはWindows10が対象です。なのでLinuxにしかできないos.fork()はやりません。情報共有も「キュー」で情報交換ができればそれでよしです。

f:id:AssistantOfKoo:20200705225604p:plain

あしからず。。。

 今回マルチプロセスする目的

目的は明確で、実験プログラムの経過表示を別プロセスのpythonプログラムにやらせて、メインのプログラムは実験に集中します。グラフだけならそんなに遅くないのですが、中間評価など実験本体ではやらないけど、グラフ化するために必要な計算が結構重い場合があるので、それを含めて別プロセスにしたいのです。まずはそのコーディングについて確認するのが、このページの趣旨です。

説明は例によって諸先輩方からご指導いただきます。

なるほど、よくわかりました。

multiprocessingと Queueを使えばよいのですね。

早速やってみましょう。

画面そのもの(インスタンス)別プロセスにする必要があるので、画面を起動してキューから情報を読み書きする処理と、主プロセスでは別プロセスとのキューを読み書きする処理をかけばよいのだと思います。

マルチプロセスで別画面を起動する

前回準備したプログラムを少し変更して、子画面を表示する処理をそのまま別プロセス起動にします。そのほかの部分は前のページを参照ください。

from multiprocessing import Process  #これを追加
...

class
MainDialog(base_main, form_main):
def __init__(self):
super(base_main, self).__init__()
self.setupUi(self)
self.setWindowTitle('main dialog')
self.lineEdit_title.setText('Main Form')
#イベントの定義をします
self.btn_child_form.clicked.connect(self.start_child_process)

#子プロセスを生成します。
self.child_process = Process(target=self.prepare_process, args=())

#子プロセスを準備します
def prepare_process(self):
self.child_form = ChildDialog()
self.child_form.show()

#子プロセスを起動します。
def start_child_process(self):
self.child_process.start()

でやってみます。いくらなんでも強引な気がしますが、やってみます。

画面は起動します。

f:id:AssistantOfKoo:20200705183556p:plain

childFormボタンをクリックすると、エラーがでました。

Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\ProgramData\Anaconda3\lib\multiprocessing\spawn.py", line 105, in spawn_main
exitcode = _main(fd)
File "C:\ProgramData\Anaconda3\lib\multiprocessing\spawn.py", line 115, in _main
self = reduction.pickle.load(from_parent)
EOFError: Ran out of input

Process finished with exit code -1073740791 (0xC0000409)

spawnの内部でエラーがでました。画面(などぐクラス)インスタンスそのものが絡む場合は別の作法が必要なのかもしれません。ネットで少し調べてわからなかったので、別の方法を考えます。ネットは今後ももう少し調べます。(これは自分ではわからない系であることを、わたくしのゴーストが伝えています)

 

他の方法として、 subprocess があるようなので、そちらを使います。

先輩先生、説明をよろしくお願いします。

 そうなのですね。subprocessでプロセスを起動できるのでね。キューではなく、標準入出力をパイプでつなぐのが基本のようです。相手プロセスのハンドリングがよくわからないので、キューはおあずけとなります。

実験プログラム側は経過をprintしているので、標準入出力のパイプ接続ではない方法を(そのうち)考えます。

#!python3
# -*- coding: utf-8 -*-

from PyQt5 import uic
import sys
from PyQt5.QtWidgets import QApplication
import numpy as np
from subprocess import Popen
from subprocess import run

# load ui file
uifile_main = './controlForm.ui'
form_main, base_main = uic.loadUiType(uifile_main)

#メインダイアログ
class MainDialog(base_main, form_main):
def __init__(self):
super(base_main, self).__init__()
self.setupUi(self)
self.setWindowTitle('main dialog')
self.lineEdit_title.setText('Main Form')
#イベントの定義をします
self.btn_child_form.clicked.connect(self.start_child_process)

#子プロセスを起動します。
def start_child_process(self):
try:
self.child_process = Popen(['python', './TEST09.py'])
#self.child_process = run(['python', './TEST09.py'])
print('trace')
print('PID:{0}'.format(self.child_process.pid)) #runのときはコメントアウト
print('Error:{0}'.format(self.child_process.errors)) #runのときはコメントアウト
except:
print('exception!')
if __name__ == '__main__':
print("start!")
app = QApplication(sys.argv)
ex = MainDialog()
ex.show()
print("end!")
sys.exit(app.exec_())

 

TEST09.pyがこのファイルそのものです。pythonの環境はアナコンダにしています。ちなみにrunのコードもテスト用に入れました。なお、runは「突き放し」ではないので、今回の目的としているマルチプロセスの恩恵はありません。

C:\ProgramData\Anaconda3\python.exe C:/Users/Mariel/PycharmProjects/imageTest/TEST09.py
start!
end!
trace
PID:10164
Error:None
start!
end!
trace
PID:37412
Error:None
start!
end!

 childFormのボタンをクリックするごとに画面が現れます。

プロセスはこんな感じです。

f:id:AssistantOfKoo:20200705222509p:plain

ボタンを押すと別プロセスで画面が起動するのですが、コレジャナイ感が半端ありません。確かにマルチプロセスですが。。。わたくし的にはキューで接続をハンドリングできて、簡単にメッセージ交換でるイメージだったのですが、ふつうに個別起動したプログラムと何らかわりません。

キューにかわるお手軽なプログラム間のメッセージ交換手段を探します。

本日の勝敗:助手の敗北

(プロセス間通信へつづく)

※肝心のグラフ表示の別プロセス化はいっこうに進みませんが、いつものことです。

今日のゲーム

ひさしぶりに家族で人生ゲーム(古典)。わたくしは教師から転職して科学者となったのですが、人生終盤で多額の借金を甥っ子に引き継ぎ、平凡な人生でおわりました。優勝者(小学生)はニートのコスプレーヤーから苦労の末工芸職人に転職、そしてすぐに人間国宝(ゲームでもっとも高給)で、科学者よりも発明・発見をして、さらにラッキーチャンスで火星ロケットを手に入れ、過去最高額を記録し上機嫌でした。