Python多处理:我怎样才能可靠地从一个subprocessredirect标准输出?

NB。 我已经看到多处理进程日志输出 – 不幸的是,它不回答这个问题。

我正在通过多处理创build一个subprocess(在Windows上)。 我希望所有的subprocess的stdoutstderr输出都被redirect到一个日志文件,而不是出现在控制台上。 我看到唯一的build议是为subprocess设置sys.stdout到一个文件。 但是,由于Windows上stdoutredirect的行为,这不会有效地redirect所有stdout输出。

为了说明问题,使用下面的代码构build一个Windows DLL

#include <iostream> extern "C" { __declspec(dllexport) void writeToStdOut() { std::cout << "Writing to STDOUT from test DLL" << std::endl; } } 

然后创build并运行如下所示的Python脚本,该脚本导入此DLL并调用该函数:

 from ctypes import * import sys print print "Writing to STDOUT from python, before redirect" print sys.stdout = open("stdout_redirect_log.txt", "w") print "Writing to STDOUT from python, after redirect" testdll = CDLL("Release/stdout_test.dll") testdll.writeToStdOut() 

为了看到和我一样的行为,可能需要针对不同于C ++运行时的C运行时构buildDLL。 就我而言,python是用Visual Studio 2010构build的,但是我的DLL是用VS 2005构build的。

我看到的行为是控制台显示:

 > stdout_test.py Writing to STDOUT from python, before redirect Writing to STDOUT from test DLL 

stdout_redirect_log.txt文件最终包含:

 Writing to STDOUT from python, after redirect 

换句话说,设置sys.stdout无法redirect由该DLL生成的stdout输出。 鉴于Windows中用于标准输出redirect的基础API的性质,这并不令人惊讶。 我之前在本机/ C ++级别遇到过这个问题,从来没有find一种方法可以在进程内可靠地redirectstdout。 它必须在外部完成。

这实际上是我启动一个subprocess的原因 – 这样我就可以在外部连接到它的pipe道,从而保证我拦截所有的输出。 我可以肯定地通过使用pywin32手动启动进程来做到这一点,但我非常希望能够使用多处理function,特别是通过多处理Pipe对象与subprocess通信的能力,以获得进展更新。 问题是,是否有任何方式为其IPC设施使用多处理, 可靠地将所有孩子的stdout和stderr输出redirect到一个文件。

更新:查看multiprocessing.Processs的源代码,它有一个静态成员,_Popen,它看起来像它可以用来覆盖用于创build过程的类。 如果它被设置为None(默认),它使用一个multiprocessing.forking.Popen,但是它看起来像

 multiprocessing.Process._Popen = MyPopenClass 

我可以重写进程创build。 但是,虽然我可以从multiprocessing.forking.Popen中得到这个,但是看起来我不得不将一些内部的东西复制到我的实现中,这听起来很片面,而且不是很有前途。 如果这是唯一的select,我想我可能会用pywin32手动完成整个事情。

Solutions Collecting From Web of "Python多处理:我怎样才能可靠地从一个subprocessredirect标准输出?"

您建议的解决方案是一个很好的解决方案:手动创建您的进程,使您可以显式访问其stdout / stderr文件句柄。 然后你可以创建一个套接字与子进程进行通信,并通过该套接字使用multiprocessing.connection(multiprocessing.Pipe创建相同类型的连接对象,所以这应该给你所有相同的IPC功能)。

这是一个双文件示例。

master.py:

 import multiprocessing.connection import subprocess import socket import sys, os ## listn for connection from remote process (and find free port number) port = 10000 while True: try: l = multiprocessing.connection.listner(('localhost', int(port)), authkey="secret") break except socket.error as ex: if ex.errno != 98: raise port += 1 ## if errno==98, then port is not available. proc = subprocess.Popen((sys.executable, "subproc.py", str(port)), stdout=subprocess.PIPE, stderr=subprocess.PIPE) ## open connection for remote process conn = l.accept() conn.send([1, "asd", None]) print(proc.stdout.readline()) 

subproc.py:

 import multiprocessing.connection import subprocess import sys, os, time port = int(sys.argv[1]) conn = multiprocessing.connection.Client(('localhost', port), authkey="secret") while True: try: obj = conn.recv() print("received: %s\n" % str(obj)) sys.stdout.flush() except EOFError: ## connection closed break 

您也可能想要查看此问题的第一个答案,以获取来自子流程的非阻塞式读取。

我不认为你有比在你的评论中提到的将子过程重定向到文件更好的选择。

控制台stdin / out / err在windows中的工作方式是每个进程在其出生时都定义了其std句柄 。 你可以用SetStdHandle来改变它们。 当你修改python的sys.stdout你只能修改python打印出来的东西,而不是其他的DLL正在打印的东西。 你的DLL中的一部分CRT正在使用GetStdHandle来找出打印出来的地方。 如果你愿意的话,你可以在你的DLL中或python32的python脚本中使用windows API。 虽然我认为在子流程中会更简单。

你遇到问题,因为过程被阻止?

看看这个 ,它使用了子进程,但是必须创建一个方法来使其不被阻塞。 我想象同样的技巧可能与多处理工作。

我假设自己脱离了基地,错过了一些东西,但是当我读到你的问题时,想到这里是值得的。

如果你可以拦截所有的stdout和stderr(我从你的问题中得到了这个印象),那么为什么不在你的每个进程中添加或者包装这个捕获功能呢? 然后通过一个队列发送什么东西到消费者,可以用所有的输出做你想做的事情?