gpt4 book ai didi

c# - 是什么过早地破坏了我的 Process.StartInfo.OutputDataReceived 回调?

转载 作者:太空宇宙 更新时间:2023-11-03 15:11:16 25 4
gpt4 key购买 nike

以下问题发生在 .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);其中 timeoutmomentproc.Start() 之后 2 分钟.这是我遇到问题的时候。非常一致地,该代码适用于产生多达几百行 stdout 的调用,但它会中断一次产生大约 7500 行的调用。发生的是 proc.WaitForExit(200);线程退出 while当我的OutputDataReceived仅约 7300 行调用了事件处理程序(这个数字再次非常一致,在测试之间仅相差 +/- 1)并且不再为其余的 stdout 行调用处理程序,所以我丢失了它们。

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

我现在推测这个问题的原因是(1)我处理每个OutputDataReceived的时间太长了。打回来。我注意到当我在事件处理程序中添加一个条件断点时,问题变得更加严重,这大大延长了方法的执行时间。我现在可以通过简单地添加 3 个没有条件断点的 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 :

When standard output has been redirected to asynchronous event handlers, it is possible that output processing will not have completed when this property returns true. To ensure that asynchronous event handling has been completed, call the WaitForExit() overload that takes no parameter before checking HasExited.

关于WaitForExit() :

This overload ensures that all processing has been completed, including the handling of asynchronous events for redirected standard output. You should use this overload after a call to the WaitForExit(Int32) overload when standard output has been redirected to asynchronous event handlers.

它表明,不带参数 调用 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com