【发布时间】:2017-04-26 14:18:29
【问题描述】:
在 .NET Framework v3.5 上出现以下问题。不知道是否适用于 v4*。
为了捕获某些进程的标准输出,我成功地使用了p.StartInfo.UseShellExecute = false; 和p.StartInfo.RedirectStandardOutput = true; 以及一个连接到p.StartInfo.OutputDataReceived+=...; 的事件处理程序。然后我打电话给p.Start(); 然后p.BeginOutputReadLine(); 然后p.WaitForExit();
到目前为止一切都很好。我按预期逐行获取事件处理程序上的所有标准输出。
我不得不引入超时而不是WaitForExit(),因为某些进程会意外触发标准输入的输入请求(例如,你确定吗?[y/n])导致我永远等待的死锁,他们也是。
我尝试的第一件事是更改为while (!p.HasExited && DateTime.Now < timeoutmom) p.WaitForExit(200);,其中timeoutmoment 在proc.Start() 之后2 分钟。这是我遇到问题的时候。非常一致的是,该代码适用于产生多达几百行标准输出的调用,但它会中断一个产生大约 7500 行的调用。发生的情况是 proc.WaitForExit(200); 线程退出 while 当我的 OutputDataReceived 事件处理程序仅被调用约 7300 行时(这个数字再次非常一致,它在测试之间仅变化 +/- 1)并且处理程序不是其余的标准输出行不再调用,所以我丢失了它们。
奇怪的是,如果我避免使用当我发布问题时,我很确定问题避免使用WaitForExit(200) 而是使用while (!p.HasExited && DateTime.Now < timeoutmom) System.Threading.Thread.Sleep(1000);(未在下面的代码中显示),问题就不会出现。Sleep(1000),但我错了。它像这样工作了几十次,然后它没有,它开始表现得就像我检查WaitForExit(200)时一样。
我现在推测这个问题的原因是(1)我处理每个OutputDataReceived回调的时间太长了。我注意到当我在事件处理程序中添加条件断点时问题更加严重,这大大延长了方法的执行时间。我现在可以通过简单地添加没有条件断点的 3x Debug.WriteLines 来重现该问题; PLUS (2) 在系统有机会在我的事件处理程序上执行所有回调之前,我访问 HasExited / WaitForExit(200) 以某种方式破坏了我的上下文。我现在在p.Start() 之后和访问任何p.* 方法之前做一个盲System.Threading.Thread.Sleep(30000),我得到了所有的回调。当我使用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
);
}
}
}
}
【问题讨论】:
标签: c# multithreading process stdout stderr