.NET Framework v3.5發生以下問題。不知道它是否適用於v4 *。什麼是過早打破我的Process.StartInfo.OutputDataReceived回調?
要捕獲的stdout一段過程我已經成功地使用p.StartInfo.UseShellExecute = false;
和p.StartInfo.RedirectStandardOutput = true;
,並掛接到p.StartInfo.OutputDataReceived+=...;
事件處理程序。然後我打電話p.Start();
,然後p.BeginOutputReadLine();
,然後p.WaitForExit();
一切都很好。如預期的那樣,我逐行獲取事件處理函數的所有stdout。
我不得不引入一個超時而不是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
);
}
}
}
}
耶穌!我不能f ***相信這一點...我遵循v3.5的文檔,其中此信息尚未被回溯。它完全符合我的問題的描述。我會嘗試一下並回複評論。最大的問題似乎是如果最終的WaitForExit()確實在HasExited變爲true之後刷新緩衝區。十分感謝你! – bogdan
.NET 3.5中可能的行爲是不同的,WaitForExit()調用未neccessary - 爲「WaitForExit(-1)」的例子行爲已更改後過.NET 3.5。不幸的是,MSDN沒有明確描述HasExited == true後WaitForExit()的行爲,所以你只能希望它能起作用。 –