微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在为 WaitForExit() 提供最大等待时间时,如何可靠地读取 c# 中进程的完整输出?

如何解决在为 WaitForExit() 提供最大等待时间时,如何可靠地读取 c# 中进程的完整输出?

考虑以下程序。在这里,我开始一个简单的过程并想处理它的输出。我以为 WaitForExit 返回后会是这种情况,但事实证明,我必须等待一整秒,直到该输出实际到达我的程序中。

static void Main(string[] args)
{
    using var p = new Process();
    p.StartInfo.FileName = "echo";
    p.StartInfo.Arguments = "I apologize for being late";
    p.StartInfo.CreateNowindow = false;
    p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.RedirectStandardError = true;
    var stdError = new StringBuilder();
    var stdOutput = new StringBuilder();
    p.ErrorDataReceived += (sender,args) => stdError.AppendLine(args.Data);
    p.OutputDataReceived += (sender,args) => stdOutput.AppendLine(args.Data);
    p.Start();
    p.BeginErrorReadLine();
    p.BeginoutputReadLine();
    // without the int-parameter here,it works
    while (!p.WaitForExit(10000))
        Console.WriteLine("still waiting...");
    string a = stdOutput.ToString();
    string b = stdError.ToString();
    Thread.Sleep(1000);
    string c = stdOutput.ToString();
    string d = stdError.ToString();
    Console.WriteLine("output before sleep: " + a);
    Console.WriteLine("error  before sleep: " + b);
    Console.WriteLine("output after  sleep: " + c);
    Console.WriteLine("error  after  sleep: " + d);
}

输出

output before sleep:
error  before sleep:
output after  sleep: I apologize for being late


error  after  sleep:

在这里,我希望 ac 具有完全相同的值。但事实并非如此。我将如何修改此示例,以便可靠地接收流程的完整输出,但不调用 Thread.Sleep(1000)

注意事项:

  • 我想要 stdOut 和 stdErr 的可靠完整输出
  • 当我使用 p.WaitForExit() 代替 p.WaitForExit(10000) 时,一切似乎都正常
  • 当对两个流都使用 p.StandardOutput.ReadToEnd() 时,它似乎有效。但有人告诉我by the official documentation,这会导致死锁
  • 在对输出使用异步解决方案的同时使用 p.StandardError.ReadToEnd() 时,输出仍然迟到。
  • 这不是 Process WaitForExit not waiting 的副本,因为对它们来说 p.WaitForExit() 没有任何参数已经不起作用。此外,他们对输出根本不感兴趣。

解决方法

这里有一个尴尬的实现细节。

打电话

p.WaitForExit();

p.WaitForExit(10000);

当实际的本机进程句柄收到信号时,做一些稍微不同的事情。

内部 p.WaitForExit(); 调用 p.WaitForExit(-1);。 -1 在这里很重要。让我们看看我们有什么(代码被简化/解释以显示本质):

public bool WaitForExit(int milliseconds)
{
    // init stuff removed
    bool flag;
    try
    {
        flag = processWaitHandle.WaitOne(milliseconds,false);
    }
    finally
    {
        // here we see our -1 return
        if (this.output != null && milliseconds == -1)
        {
            this.output.WaitUtilEOF();
        }
    }
    return flag;
}

在上面的代码片段中,您可以看到 this.output.WaitUtilEOF(); 并调用了一个使用队列的内部 AsyncStreamReader。对 WaitUtilEOF(); 的调用基本上是在流上等待引发 EOF 事件。

我找不到其他方法来强制 Process 类进行调用以等待那些 EOF 事件。唯一的选择是不带参数调用 WaitForExit()。然而,在调用 WaitForExit(); 返回后调用 WaitForExit(10000) 不会受到惩罚。

因此,如果您在第一个 WaitForExit(10000) 上超时,但您确定您宁愿等待更长时间,让 AsyncStreamReader 将其拥有的所有数据交给您,请在不带参数的情况下调用 WaitForExit()两个 AsyncStreamReader 都清空它们的队列,然后将控制权返回给您。这确实意味着,如果您的进程没有结束,您现在就会陷入等待,除非您自己杀死子进程,否则它将永远无法自行解决。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。