.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);
,其中timeoutmoment
是proc.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/