为什么异步读取在进程终止后仍未完成?

4

我编写了一个进程,它从作为参数给定的文件中读取数据。我异步读取了StandardOutput,并同步读取了StandardError。

public static string ProcessScript(string command, string arguments)
{
        Process proc = new Process();
        proc.StartInfo.UseShellExecute = false;
        proc.StartInfo.RedirectStandardOutput = true;
        proc.StartInfo.RedirectStandardError = true;
        proc.StartInfo.FileName = command;
        proc.StartInfo.Arguments = arguments;
        proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; 
        proc.Start();
        string error = null;
        string output = null;
        proc.OutputDataReceived += (sender, outputLine) => 
        { 
            if (outputLine.Data != null) 
            {
                output += outputLine.Data;
            }
        };
        proc.BeginOutputReadLine();
        error = proc.StandardError.ReadToEnd();
        proc.WaitForExit();
        proc.Close();

        //I have not got entire Output
        return output;
} 

在流程完成后我得到了输出,但并非全部。我只得到了部分数据。异步读取操作在进程完成任务后仍未结束,所以我只得到了部分数据。我需要完整的字符串。

编辑:

我正在使用 .Net 3.5。我不能使用ReadToEndAsync方法。

有什么想法吗?


1
同时使用 OutputDataReceived 和 BeginOutputReadLine() 是一个错误。 - Hans Passant
@HansPassant:谢谢你指出来。但是在这个例子中http://msdn.microsoft.com/en-us/library/vstudio/system.diagnostics.process.beginoutputreadline,我发现两者都被使用了。 - BinaryMee
2
@HansPassant,你能详细解释一下吗?BeginOutputReadLine用于使事件“实时”。它不会返回任何东西。Process类设计有误。 - usr
@usr:是的。我也使用了一些线程延迟来读取完整的输出。但数据可能会有所不同,对于某些进程来说,这种线程延迟看起来像是一个不必要的延迟。这就是为什么我在寻找其他选项。 - BinaryMee
Process.WaitForExit() 与 BeginOutputReadLine() 互锁,直到检测到文件末尾才会返回。而使用 OutputDataReceived 只是为问题增加随机性。 - Hans Passant
显示剩余2条评论
3个回答

6

与其处理事件并解决随之而来的问题,您可以直接从实际输出流中读取(假设您使用的是.NET 4.5,由于其添加了异步功能)。

public static string ProcessScript(string command, string arguments)
{
    Process proc = new Process();
    proc.StartInfo.UseShellExecute = false;
    proc.StartInfo.RedirectStandardOutput = true;
    proc.StartInfo.RedirectStandardError = true;
    proc.StartInfo.FileName = command;
    proc.StartInfo.Arguments = arguments;
    proc.Start();

    var output = proc.StandardOutput.ReadToEndAsync();
    var error = proc.StandardError.ReadToEndAsync();
    proc.WaitForExit();
    proc.Close();
    var errorContent = error.Result;
    return output.Result;
}

在这里,由ReadToEndAsync表示的Task实际上直到其结果所代表的所有数据都完全存在之前都不会完成。这意味着你要等待所有数据都准备好,而不是等待进程完成,因为这两者可能不是完全同时发生的。


我正在使用 .Net 3.5 :( - BinaryMee

0

0

根据这篇文章,在WaitForExit完成后事件处理程序可能会触发。我试图使用Reflector找出如何实现,但是我没有看到它。无论如何,我自己也经历过这种情况。

这篇文章还揭示了如何处理这个问题的秘密:显然,当不再有输入时,事件处理程序将被调用并传入null数据。因此,您需要等待这个条件。

我认为Servy处理这个问题的方式更好。我只是在这里记录这种行为。


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