写入 Process.StandardInput 时出现死锁问题

9

我是一名开发者,我在应用程序开发中遇到了死锁问题。

我的代码看起来像这样:

Process p = new Process(); // That using an other application

然后我将发送一个 .xml 文件到该进程:
XmlSerializer xs = new XmlSerializer(data.GetType());
using (var ms = new MemoryStream())
{
    var sw = new StreamWriter(ms);
    XmlWriter xmlwriter = XmlWriter.Create(sw, xmlWriterSettings);
    xmlwriter.WriteProcessingInstruction("PipeConfiguratorStyleSheet", processing);
    xs.Serialize(xmlwriter, data);
    xmlwriter.Flush();
    ms.Position = 0;
    var sr = new StreamReader(ms);
    while (!sr.EndOfStream)
    {
        String line = sr.ReadLine();
        p.StandardInput.WriteLine(line);                
        Console.WriteLine(line);
        p.BeginOutputReadLine();
        p.CancelOutputRead(); 
    }
}

实际上,我可以将我的.xml文件的一部分发送给进程,但在某个点上,我会遇到死锁。我猜我不知道如何正确使用BeginOutputReadLine()


你确定接收端正在读取数据吗?如果缓冲区已满但接收端没有读取,可能会被阻塞。你有接收端的代码吗? - Scott Chamberlain
你如何遇到死锁?我问这个问题是因为没有“锁”,所以对我来说不是很明显,应该如何出现挂起(可以描述为死锁)... - user57508
我不是很清楚。我使用了基本的Write() / Read()方法,但它是同步的。根据我的研究,我必须使用异步方法,如BeginOutputReadLine()。 - stbr
在这段代码中,我正在打印发送到我的进程的数据,它是“line”变量,对应于我的.xml文件的每一行。因此,在控制台中,我的程序开始打印可能是100-200行的xml,但仍然停留在1个“标准”数据行。我认为这是因为我的进程发送了一个答案,而我没有正确地读取它。 - stbr
在我看来,这是一个将数据发送到我的进程的管道,因此我发送了数据,但我从未读取过我的进程的答案。所以我可能需要刷新我的StandardOutput()。但是如何做呢:s - stbr
显示剩余7条评论
1个回答

3

首先,为什么不直接使用Process.StandardInput属性作为您的目标呢,就像这样:

var process = new Process
{
    // all your init stuff
};
var xmlSerializer = new XmlSerializer(data.GetType());
var xmlwriter = XmlWriter.Create(process.StandardInput, xmlWriterSettings);
xmlSerializer.Serialize(xmlwriter, data);

否则,msdn-entry提供了一个清晰的如何使用Process.BeginOutputReadLine()的方法,你可以重新设计它。
var autoResetEvent = new AutoResetEvent(false); // this mutex acts as our bouncer for the reading-part
var process = new Process
{
    // all your init stuff
};
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (sender, args) => {
    // TODO you could read the content here with args.Data
    autoResetEvent.Set();
};
process.Start();

using (var memoryStream = new MemoryStream())
{
    using (var streamWriter = new StreamWriter(memoryStream))
    {
        var xmlSerializer = new XmlSerializer(data.GetType());
        var xmlwriter = XmlWriter.Create(streamWriter, xmlWriterSettings);
        xmlSerializer.Serialize(xmlwriter, data);
    }
    memoryStream.Position = 0;
    using (var streamReader = new StreamReader(memoryStream))
    {
        while (!streamReader.EndOfStream)
        {
            var line = streamReader.ReadLine();
            process.StandardInput.WriteLine(line);

            Console.WriteLine(line);

            process.BeginOutputReadLine();
            autoResetEvent.WaitOne();
        }
    }
}

// TODO closing the process.StandardInput, exiting process, ...

无论如何 - 我知道这应该是一条评论 - 你等待进程写入东西是否有具体原因?

StandardOutput 流可以同步或异步读取。Read、ReadLine 和 ReadToEnd 等方法在进程的输出流上执行同步读操作。这些同步读操作不会完成,直到关联的进程向其 StandardOutput 流写入内容或关闭流为止。相比之下,BeginOutputReadLine 在 StandardOutput 流上启动异步读操作。此方法使指定的事件处理程序能够处理流输出,并立即返回给调用方,调用方可以在流输出被定向到事件处理程序时执行其他工作。

这意味着,如果您的进程没有写任何内容(而且您正在等待),那么您将无限期地等待响应... 编辑 您还应添加一个 Process.ErrorDataReceived 处理程序,例如:
process.StartInfo.RedirectStandardError = true;
process.ErrorDataReceived += (sender, args) => {
    // TODO do something with the response of args.Data
    autoResetEvent.Set();
};

并且

while (!streamReader.EndOfStream)
{
    var line = streamReader.ReadLine();
    process.StandardInput.WriteLine(line);

    Console.WriteLine(line);

    process.BeginOutputReadLine();
    process.BeginErrorReadLine();
    autoResetEvent.WaitOne();
}

还需要处理错误情况(无论是什么意思)。


程序对我来说不起作用。我会尝试使用您的答案解决错误并发送最终代码。非常感谢。 - stbr
嗨,打印了一些数据后,我遇到了这个错误:流上已经启动了异步读取操作。你知道怎么解决吗?谢谢。 - stbr
@SteevenBrunner 当你在没有先调用.OutputDataReceived的情况下调用.BeginOutputReadLine()时会发生这种情况。这就是为什么我使用了AutoResetEvent。你实际上尝试了哪种方法,目前又在使用哪种方法?你能把这些信息加到你的问题中吗? - user57508
我的程序现在可以工作了。问题是我没有读取错误输出,我的进程返回了一些警告,导致执行被阻塞。我没有使用autoResetEvent(),因为我的进程并不总是返回结果。非常感谢您的帮助 =) - stbr
@SteevenBrunner 我已经修改了我的答案,并添加了一个添加错误处理程序的提示。 - user57508

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接