.NET Framework v3.5上会出现以下问题。不知道它是否适用于v4 *。

为了捕获某些进程的stdout,我成功地使用了p.StartInfo.UseShellExecute = false;p.StartInfo.RedirectStandardOutput = true;以及连接到p.StartInfo.OutputDataReceived+=...;的事件处理程序。然后我先依次调用p.Start();p.BeginOutputReadLine();p.WaitForExit();
到目前为止一切都很好。我按预期逐行获取了事件处理程序上的所有stdout。

我不得不引入一个超时而不是WaitForExit(),因为某些进程意外地触发了stdin上的输入请求(例如,您确定吗?[y/n])导致死锁,我将永远等待,所以也是如此。

我尝试的第一件事是更改为while (!p.HasExited && DateTime.Now < timeoutmom) p.WaitForExit(200);,其中timeoutmomentproc.Start()之后2分钟。这是我遇到问题的时候。该代码非常一致地适用于产生多达几百行标准输出的调用,但对于一次产生约7500行的调用却中断了。当我的proc.WaitForExit(200);事件处理程序仅被调用约7300行(此数字再次非常一致,在两次测试之间仅相差+/- 1)时,while线程退出了OutputDataReceived。标准输出线,所以我输了。

奇怪的是,如果我避免使用WaitForExit(200)而不是使用while (!p.HasExited && DateTime.Now < timeoutmom) System.Threading.Thread.Sleep(1000);(在下面的代码中未显示),则不会出现该问题。当我发布问题时,我很确定可以使用Sleep(1000)避免了该问题,但是我错了。它像这样工作了几十次,然后却没有,就像我检查WaitForExit(200)一样开始表现。

现在,我推测此问题的原因是(1)我花太长时间来处理每个OutputDataReceived回调。我注意到,当我在事件处理程序中添加条件断点时,问题变得更加严重,这大大延长了方法的执行时间。我现在可以通过简单地添加3x Debug.WriteLines而无需条件断点来重现该问题;加上(2),在系统有机会对事件处理程序执行所有回调之前,我访问HasExited/WaitForExit(200)会破坏我的上下文。现在,我在System.Threading.Thread.Sleep(30000)之后和访问任何p.Start()方法之前,先做一个盲人的p.*,然后得到所有回调。当我使用WaitForExit()时,似乎我可能需要花费很多时间来处理每个回调,但我仍然会全部使用它们。

有人可以对此有更多的了解吗?

代码:

    private int _execOsProc(
        ProcessStartInfo Psi
        , string SecInsensArgs
        , TextWriter ExtraStdOutAndErrTgt
        , bool OutputToExtraStdOutOnly
        )
    {
        var pr = new Process();
        pr.StartInfo = Psi;
        pr.StartInfo.UseShellExecute = false;
        pr.StartInfo.RedirectStandardOutput = pr.StartInfo.RedirectStandardError = true;
        pr.StartInfo.CreateNoWindow = true;
        var ol = new DataReceivedEventHandler(this._stdOutDataReceived);
        var el = new DataReceivedEventHandler(this._stdErrDataReceived);
        pr.OutputDataReceived += ol;
        pr.ErrorDataReceived += el;
        try
        {
            __logger.Debug("Executing: \"" + pr.StartInfo.FileName + "\" " + SecInsensArgs);
            if (ExtraStdOutAndErrTgt == null)
            {
                this.__outputToExtraStdOutOnly = false;
            }
            else
            {
                this.__extraStdOutAndErrTgt = ExtraStdOutAndErrTgt;
                this.__outputToExtraStdOutOnly = OutputToExtraStdOutOnly;
            }
            pr.Start();
            pr.BeginOutputReadLine();
            pr.BeginErrorReadLine();
            var startmom = DateTime.Now;
            var timeoutmom = startmom.AddMinutes(2);
            while (!pr.HasExited && DateTime.Now < timeoutmom) pr.WaitForExit(200);
            pr.CancelOutputRead();
            pr.CancelErrorRead();
            if (pr.HasExited)
            {
                __logger.Debug("Execution finished with exit status code: " + pr.ExitCode);
                return pr.ExitCode;
            }
            else
            {
                __logger.Debug("Timeout while waiting for execution to finish");
                pr.Kill();
                return -100;
            }
        }
        finally
        {
            pr.OutputDataReceived -= ol;
            pr.ErrorDataReceived -= el;
            if (this.__extraStdOutAndErrTgt != null)
            {
                this.__extraStdOutAndErrTgt = null;
                this.__outputToExtraStdOutOnly = false;
            }
        }
    }

    private void _stdOutDataReceived(
        object sender
        , DataReceivedEventArgs e
        )
    {
        string rdata = string.IsNullOrEmpty(e.Data) ? "" : e.Data.Trim();
        if (!this.__outputToExtraStdOutOnly) __logger.Debug("SO: " + rdata);
        if (this.__extraStdOutAndErrTgt != null)
        {
            lock (this.__extraStdOutAndErrTgt)
            {
                try
                {
                    this.__extraStdOutAndErrTgt.WriteLine(rdata);
                    this.__extraStdOutAndErrTgt.Flush();
                }
                catch (Exception exc)
                {
                    __logger.Warn(
                                "WARNING: Error detected but ignored during extra stream write"
                                    + " on SODR. Details: " + exc.Message
                                , exc
                                );
                }
            }
        }
    }

    private void _stdErrDataReceived(
        object sender
        , DataReceivedEventArgs e
        )
    {
        string rdata = string.IsNullOrEmpty(e.Data) ? "" : e.Data.Trim();
        if (!__outputToExtraStdOutOnly) __logger.Debug("SE: " + rdata);
        if (this.__extraStdOutAndErrTgt != null)
        {
            lock (this.__extraStdOutAndErrTgt)
            {
                try
                {
                    this.__extraStdOutAndErrTgt.WriteLine(rdata);
                    this.__extraStdOutAndErrTgt.Flush();
                }
                catch (Exception exc)
                {
                    __logger.Warn(
                                "WARNING: Error detected but ignored during extra stream write"
                                    + " on SEDR. Details: " + exc.Message
                                , exc
                                );
                }
            }
        }
    }

最佳答案

我不确定它是否可以解决问题,但是将其发布在评论中太长了。

MSDN说到Process.HasExited:



关于WaitForExit():



它表明,调用不带参数的WaitForExit()应该可以解决该问题。就像是:

var startmom = DateTime.Now;
var timeoutmom = startmom.AddMinutes(2);
while (!pr.HasExited && DateTime.Now < timeoutmom)
    pr.WaitForExit(200);

if (pr.HasExited)
{
    WaitForExit();//Ensure that redirected output buffers are flushed

    pr.CancelOutputRead();
    pr.CancelErrorRead();

    __logger.Debug("Execution finished with exit status code: " + pr.ExitCode);
    return pr.ExitCode;
}
else
{
    pr.CancelOutputRead();
    pr.CancelErrorRead();

    __logger.Debug("Timeout while waiting for execution to finish");

    pr.Kill();
    return -100;
}

关于c# - 是什么过早打破了我的Process.StartInfo.OutputDataReceived回调?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41088608/

10-09 00:56