同步读取进程中的数据';s的空stdout会导致死锁
本文关键字:stdout 的空 死锁 取进程 读取 数据 同步 | 更新日期: 2023-09-27 17:57:29
我在设置一个创建python进程并与之交互的c#应用程序时遇到了问题1。下面给出一个简单的例子。
编辑:对SO的进一步研究表明,我的问题可能是重复的。这里和这里讨论了.NET框架中一个潜在的相关已知错误。早在2014年,唯一简单的解决方法似乎就是要求子进程在stdOut和stdErr中都写一些东西。但我想知道这个假设是否正确,并想知道自2014年以来是否没有解决方案?
我必须满足以下边界条件:
- 在交出脚本或命令后,我无法关闭python进程,但我必须保持该进程的活力编辑:因此,我无法使用Process.WWaitForExit()方法
- 由于std一直处于打开状态,我相信我无法检查EndOfStream,因为这需要读取到流的末尾,而流并不存在
- 此外,我的应用程序必须等待python进程的响应,因此使用带有OnOutputDataReceived的BeginOutputReadLine()异步选项似乎不适合我
- 由于将发送到python的命令是任意用户输入,因此python的结果可能在stdOut或stdErr中("4+7"导致stdOut中存储的"11";"4+a"导致stdErr的"名称‘a’未定义")
我所做的是:
- 在交互模式下设置python进程(参数"-i")
- 启用StdIn、Out和Err的重定向
- 启动流程
- 获取标准的StreamReaders和Writers
之后,我想首先检查StdOut和StdErr。我知道python将以下信息写入StdErr
Python 2.7.11(v2.7.11:6d1b6a68f7752015年12月5日20:32:19)[MSCv.1500 32位(英特尔)]在win32 上
我可以通过使用errorReader.Peek()并从errorReader2读取基于字符的代码来获得这行代码。
然而,另一个过程的情况可能完全不同。即使使用Python,我也会遇到以下问题:当我想最初从outputReader读取时,其中没有任何内容,outputReader.Peek()似乎会陷入死锁。如上所述,outputReader.EndOfStream或outputReadr.ReadToEnd()也是如此。那么,我如何知道stdOut是否可以在不导致死锁的情况下使用呢?
代码:
// create the python process StartupInfo object
ProcessStartInfo _tempProcessStartInfo = new ProcessStartInfo(@"C:'TMP'Python27'python.exe");
// ProcessStartInfo _tempProcessStartInfo = new ProcessStartInfo(PathToPython + "python.exe");
// python uses "-i" to run in interactive mode
_tempProcessStartInfo.Arguments = "-i";
// Only start the python process, but don't show a (console) window
_tempProcessStartInfo.WindowStyle = ProcessWindowStyle.Minimized;
_tempProcessStartInfo.CreateNoWindow = true;
// Enable the redirection of python process std's
_tempProcessStartInfo.UseShellExecute = false;
_tempProcessStartInfo.RedirectStandardOutput = true;
_tempProcessStartInfo.RedirectStandardInput = true;
_tempProcessStartInfo.RedirectStandardError = true;
// Create the python process object and apply the startupInfos from above
Process _tempProcess = new Process();
_tempProcess.StartInfo = _tempProcessStartInfo;
// Start the process
bool _hasStarted = _tempProcess.Start();
//// ASynch reading seems not appropriate to me:
// _tempProcess.BeginOutputReadLine();
// _tempProcess.BeginErrorReadLine();
// Create StreamReaders and Writers for the Std's
StreamReader outputReader = _tempProcess.StandardOutput;
StreamReader errorReader = _tempProcess.StandardError;
StreamWriter commandWriter = _tempProcess.StandardInput;
// Create StringBuilder that collects results and ErrorMessages
StringBuilder tmp = new StringBuilder("");
// Create temp variable that is used to peek into streams. C# uses -1 to indicate that there is no more byte to read
int currentPeek = -1;
// Get Initial Error Message. In this specific case, this is the python version
tmp.AppendLine("INITIAL ERROR MESSAGE:");
currentPeek = errorReader.Peek();
while (currentPeek >= 0)
{
char text = (char)errorReader.Read();
tmp.Append(text);
currentPeek = errorReader.Peek();
}
// Get initial output Message. In this specific case, this is EMPTY, which seems to cause this problem, as ...
tmp.AppendLine("INITIAL STDOUT MESSAGE:");
//// ... the following command CREATES a well defined output, and afterwards everything works fine (?) but ...
//commandWriter.WriteLine(@"print 'Hello World'");
//// ... without the the above command, neither
//bool isEndOfStream = outputReader.EndOfStream;
//// ... nor
// currentPeek = outputReader.Peek();
//// ... nor
// tmp.AppendLine(outputReader.ReadLine());
//// ... nor
//tmp.AppendLine(outputReader.ReadToEnd());
//// ... works
// Therefore, the following command creates a deadlock
currentPeek = outputReader.Peek();
while (currentPeek >= 0)
{
char text = (char)outputReader.Read();
tmp.Append(text);
currentPeek = errorReader.Peek();
}
_currentPythonProcess = _tempProcess;
return true;
1解决这个非常具体的问题的一个简单方法是先向进程发送一个有效的命令,例如简单的"4",它也会返回一个"4"。。。然而,我想了解进程流、管道和相应的读写器是如何工作的,以及我如何在C#中使用它们。谁知道未来会发生什么,也许当蟒蛇的响应是2^n+1字节长时,我会遇到缓冲区问题。。。2我知道我也能读基于行的书。但是,Peek()阻止我报告与截断行有关的问题。
如果您可以等待进程结束,然后读取缓冲区,您可能可以使用process.WaitForExit。您还可以检查另一种方法process.WaitTorInputIdle,但这取决于进程是否有消息循环,我认为Python
脚本在执行时不会得到消息循环。