python如何启动新进程,并获取程序的输出.

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。

第二种:也是最简单优雅的一种写法. 这种写法会把输出写入到文件中.
这种方式比较稳定, 不需要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
           )

第三种方法, 尝试自己写一个包装类把输出到文件的功能包装起来.中间拦截下Write操作. 后来事实证明, 这种思路是错误的. 因为无论你怎么包装, 子进程中的输出是不会自动到主进程中的. 在内存中是完全独立的两个对象. 子进程中的日志输出操作是直接使用传递过去的文件句柄操作的.使用的是操作系统基本的操作, 跟父进程基本上的python代码无关, 所以包装也没啥用.

第四种, 新建一个线程循环读取子进程的管道输出, 在第一种的方法基础上将参数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()
         

第五种方法, 在第二种输出到文件的方法的基础上. 循环读取子进程输出的日志文件. 然后自己解析输出内容. 缺点是每次都要从头读取, 比较慢.
最好也是在子线程中读取. 对磁盘的操作比较大.

总结:
个人感觉就目前来看,既要实时又要稳定,最好的做法是第四种, 开子线程循环读取管道信息. 并在子线程中处理输出的日志.

猜你喜欢

转载自blog.csdn.net/phker/article/details/110386167