来自Process的空stdout的同步读取数据会导致死锁

时间:2016-07-18 06:57:54

标签: c# ipc stdout peek

我在设置一个创建并与python进程1交互的c#应用程序时遇到了问题。下面给出一个简单的例子。

编辑:对SO的进一步研究揭示我的问题可能是重复的。 .NET Framework中可能存在的相关已知错误包括herehere。似乎早在2014年,唯一简单的解决方法确实是要求子进程在stdOut和stdErr中编写内容。但我想知道这个假设是否正确,并想知道自2014年以来是否还没有修复?

我必须满足以下边界条件:

  1. 我无法在移交脚本或命令后关闭python进程,但我必须让进程保持活动状态。 编辑:因此我无法使用Process.WaitForExit()方法
  2. 由于std的所有时间都保持打开状态,我相信我无法检查EndOfStream,因为这需要读取到流的末尾,而不存在。
  3. 此外,我的应用程序必须等待python进程的响应,因此使用带OnOutputDataReceived的BeginOutputReadLine()的异步选项似乎不适合我。
  4. 由于将发送给python的命令是任意用户输入,因此pythons结果可能是stdOut或stdErr(" 4 + 7"结果" 11"存储在stdOut中;" 4 + a"结果"名称' a'未定义"在stdErr中)
  5. 我的目的是:

    • 以交互模式设置python进程(Argument" -i")
    • 启用StdIn,Out和Err的重定向
    • 启动流程
    • 为Std&#39>获取StreamReaders和Writers

    之后,我想先检查StdOut和StdErr。我知道 python将以下信息写入 StdErr

      

    Python 2.7.11(v2.7.11:6d1b6a68f775,2015年12月5日,20:32:19)[MSC   v1500 32位(英特尔)]在win32上

    我可以通过使用errorReader.Peek()并从errorReader 2读取基于字符的内容来获取此行。

    然而,另一个过程的情况可能完全不同。即使使用Python,我也会遇到以下问题:当我想从outputReader初始读取时,它中没有任何内容,并且outputReader.Peek()似乎遇到了死锁。如上所述,对于outputReader.EndOfStream或outputReader.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#中使用它们。谁知道未来会带来什么,也许当pythons响应长度为2 ^ n + 1个字节时,我遇到缓冲问题... 2我知道我也可以阅读基于行的。但是,Peek()阻止我报告与截断行相关的问题。

1 个答案:

答案 0 :(得分:0)

如果您可以等待进程结束然后再读取缓冲区,则可以使用Process.WaitForExit。您还可以检查另一种方法Process.WaitForInputIdle,但这取决于具有消息循环的进程,我不认为<?php $fileName = preg_replace("#\?.*$#", "", basename("http://localhost/mc/site-01-up/index.php?c=lorem-ipsum")); echo $fileName; // DISPLAYS: index.php // OR SHORTER AND SIMPLER: $fileName = basename($_SERVER['PHP_SELF']); echo $fileName; // DISPLAYS: index.php 脚本在执行时会得到。