Pythonはどのようにして新しいプロセスを開始し、プログラムの出力を取得しますか。
最初の、もっと嫌な方法は、メインプロセスをブロックし、それを読まないと、ログをフリーズすることがよくあります。
import shlex
import subprocess
if __name__ == '__main__':
shell_cmd = 'python3 subprogram.py'
cmd = shlex.split(shell_cmd)
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while p.poll() is None: # 这里死循环了.
line = p.stdout.readline() # readlines(100)
line = line.strip()
if line:
print('Subprogram output: [{}]'.format(line))
if p.returncode == 0:
print('Subprogram success')
else:
print('Subprogram failed')
subprocess.Popenでsubprocess.PIPEを使用する場合は、readlines()を使用してパイプラインの内容を読み取ると、プログラムがブロックされます。この場合、readlines(n)でパラメーターnを設定するだけで済みます。
例:p = subprocess.Popen(['nethogs'、 '-t'、 '-d']、stdout = subprocess.PIPE)
x = p.stdout.readlines(100)
すべてのコンテンツを取り出したい場合パイプラインでは、パラメータnを非常に大きく設定するだけで済みます。結局のところ、subprocess.PIPEの最大容量はわずか64kbです。
2番目のタイプ:これは最も単純で最も洗練された書き込み方法でもあります。
この書き込み方法は出力をファイルに書き込みます。この方法は比較的安定しており、実行にwhileループを必要としません。ブロックの問題はありません。メインスレッド。欠点は、出力をリアルタイムで取得できないことです。、ログファイルを自分で読み取る必要があります。
import subprocess
Logfilename = str(time.time())+"std.log"
with io.open(Logfilename, 'w',encoding='utf-8' ) as writer:
self.proc = subprocess.Popen(
[
"python.exe",
"./UNet/pdseg/train.py",
"--use_gpu",
"--cfg=./static/dataset/optic_disc_seg/_UNetAIModels/M1/unet_optic.yaml",
],
shell = True,
stdout=writer
)
3番目の方法は、出力をファイル関数にラップするラッパークラスを書き込もうとすることです。途中で書き込み操作をインターセプトします。後で、この考え方が間違っていることがわかります。どのようにラップしても、出力はサブプロセスはメインプロセスに自動的に送信されません。2つのオブジェクトはメモリ内で完全に独立しています。子プロセスのログ出力操作は、過去に渡されたファイルハンドルによって直接操作されます。オペレーティングシステムの基本操作が使用されます。 、これは基本的に親プロセスのコードと同じです。上記のpythonコードは無関係であるため、パッケージ化は役に立ちません。
4つ目は、ループ内のサブプロセスのパイプライン出力を読み取るための新しいスレッドを作成することです。最初の方法に基づいて、パラメーターstdoutをsubprocess.PIPEに設定し、メインスレッドがスタックしないようにします。それほど美しくはなく、より多くのリソースが必要だと感じてください。
#读取日志的线程
class ReadLogThread(threading.Thread):
def __init__(self, _pipe, onout):
threading.Thread.__init__(self)
self._pipe = _pipe
self.logList = []
self.onout = onout
def run(self):
while True:
try:
line = self._pipe.readline()
if line == "":
break
self.logList.append(line)
if(self.onout!=None):
self.onout(line)
# print("Thread=>",line)
except Exception as eee:
traceback.print_exc()
# self.logList.append(str(eee))
def get_log(self):
return "".join(self.logList)
def main():
writer = io.StringIO(initial_value='',NEWLINE='\n')
self.proc = subprocess.Popen(
arg,
# [
# "python.exe",
# "./UNet/pdseg/train.py",
# "--use_gpu",
# "--cfg=./static/dataset/optic_disc_seg/_UNetAIModels/M1/unet_optic.yaml",
# ],
shell = True,
stdout=subprocess.PIPE
)
循环输出标准输出内容,避免阻塞
self.stdoutThread = ReadLogThread(self.proc.stdout,onStdoutCallBack)
self.stdoutThread.start()
self.stderrThread = ReadLogThread(self.proc.stderr,onStderrCallBack)
self.stderrThread.start()
5番目の方法は、2番目のファイル出力方法に基づいています。子プロセスによって出力されたログファイルをループで読み取り、出力内容を自分で解析します。欠点は、毎回最初から読み取る必要があることです。これは遅いです。
サブスレッドでの読み取りも最適です。ディスク操作は比較的大規模です。
要約:
個人的には、現在の観点から、リアルタイムで安定している必要があります。最良の方法は、サブスレッドを開いてパイプライン情報をループで読み取ることです。そして、サブスレッドで出力ログを処理します。糸。