从Process的空stdout同步读取数据会导致死锁 [英] Synchronized reading data from Process's empty stdout causes deadlock

查看:120
本文介绍了从Process的空stdout同步读取数据会导致死锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法设置可创建python进程并与python进程交互的ac#应用程序

I am having trouble setting up a c# application that creates and interacts with a python process1. A simplistic example is given below.

编辑:对SO的进一步研究表明,我的问题可能是重复的.在 此处.似乎在2014年,唯一简单的解决方法确实是要求子进程在stdOut和stdErr中都编写一些内容.但是我想知道这个假设是否正确,并想知道自2014年以来是否没有解决方案?

Further research on SO unveiled that my question is a possible duplicate. A potentially related known bug in the .NET Framework is duscussed here and here. It seems that back in 2014 the only easy workaround is indeed to ask the child process to write something in both stdOut and stdErr. But I'd like to know if this assumption is correct and wonder if there hasn't been a fix since 2014?

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

I have to fulfill the following boundary conditions:

  1. 在交出脚本或命令后,我无法关闭python进程,但我必须保持该进程为活动状态. 编辑:因此,我无法使用Process.WaitForExit()方法
  2. 由于std始终保持打开状态,我认为我无法检查EndOfStream,因为这将需要读取到流的末尾(不存在).
  3. 此外,我的应用程序必须等待python进程的响应,因此将BeginOutputReadLine()与OnOutputDataReceived一起使用的异步选项对我来说似乎不合适.
  4. 由于要发送到python的命令是任意用户输入,因此pythons结果可能在stdOut或stdErr中("4 + 7"的结果存储在stdOut中的"11";"4 + a"的结果显示为"name"在stdErr中未定义"a")
  1. I am not able to close the python process after handing over a script or a command, but I have to keep the process alive. For that reason I can not make use of the Process.WaitForExit() Method
  2. As the std's remain open all the time, I believe I can't check for EndOfStream, as that would require to read to the end of the stream, which does not exist.
  3. Furthermore, my application has to wait for the response of the python process, therefore the asynchronous option using BeginOutputReadLine() with OnOutputDataReceived seems not appropriate to me.
  4. As the commands that will be sent to python are arbitrary user input, pythons result might be either in stdOut or stdErr ("4+7" results in "11" stored in stdOut; "4+a" results in "name 'a' is not defined" in stdErr)

我要做的是:

  • 以交互模式(参数"-i")设置python进程
  • 启用StdIn,Out和Err的重定向
  • 开始该过程
  • 获取Std的StreamReaders和Writers

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

After that, I want to initially check the StdOut and StdErr. I know that python writes the following piece of information to the StdErr

Python 2.7.11(v2.7.11:6d1b6a68f775,2015年12月5日,20:32:19)[MSC v32上的v.1500 32位(Intel)]

Python 2.7.11 (v2.7.11:6d1b6a68f775, Dec 5 2015, 20:32:19) [MSC v.1500 32 bit (Intel)] on win32

并且我能够使用errorReader.Peek()并从errorReader中读取基于字符的行

and I am able to get this line by using errorReader.Peek() and reading character-based from the errorReader 2.

但是,另一个过程的情况可能完全不同.即使使用Python,我也会遇到以下问题:当我最初要从outputReader读取时,其中没有任何内容,并且outputReader.Peek()似乎陷入了僵局.如上所述,outputReader.EndOfStream或outputReader.ReadToEnd()同样适用.那么我怎么知道是否可以完全使用stdOut 而不会导致死锁?

However, the situation with another process might be totally different. Even with Python, I run into the following problem: when I want to initially read from the outputReader, there is nothing contained in it and outputReader.Peek() seems to run into a deadlock. As mentioned above, the same holds for outputReader.EndOfStream or outputReader.ReadToEnd(). So how do I know if the stdOut can be used at all without causing a deadlock?

代码:

        // 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 An easy fix to this very specific problem is to send a valid command to the process first, for example simply "4", which returns a "4" as well... However, I want to understand how process streams, pipes and the corresponing readers and writers work and how I can use them in C#. Who knows what future brings, maybe I run into buffer problems when pythons response is 2^n+1 bytes long... 2 I know that I can also read line-based. However, the Peek() prevents me from reporting problems that are related to truncated lines.

推荐答案

如果可以等待过程结束然后读取缓冲区,则可以使用 Process.WaitForInputIdle ,但这取决于具有消息循环的进程,我认为执行时不会获得Python脚本.

If you can wait for the process to end and then read the buffers, you might be able to use Process.WaitForExit. There is also another method you can check, Process.WaitForInputIdle, but it depends on the process having a message loop, which I don't think a Python script gets when executing.

这篇关于从Process的空stdout同步读取数据会导致死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆