为什么我的循环使用 100% CPU 并且永远不会结束?
-
22-07-2019 - |
题
我有这个方法:
private delegate void watcherReader(StreamReader sr);
private void watchProc(StreamReader sr) {
while (true) {
string line = sr.ReadLine();
while (line != null) {
if (stop) {
return;
}
//Console.WriteLine(line);
line = stripColors(line);
txtOut.Text += line + "\n";
line = sr.ReadLine();
}
}
}
它从进程(cmd.exe)读取流。当用户关闭cmd.exe窗口时,会导致CPU使用率跳至100%。当使用调试器时,我发现它停止在 sr.ReadLine() 上并且永远不会返回。因为它同时监视 StandardErrorStream 和 StandardOutputStream,所以它在两个内核上都使用 100%。
如果您需要的话,这里还有该项目的更多代码。
[DllImport("User32")]
private static extern int ShowWindow(int hwnd, int nCmdShow); //this will allow me to hide a window
public ConsoleForm(Process p) {
this.p = p;
p.Start();
ShowWindow((int)p.MainWindowHandle, 0); //0 means to hide the window.
this.inStream = p.StandardInput;
this.outStream = p.StandardOutput;
this.errorStream = p.StandardError;
InitializeComponent();
wr = new watcherReader(watchProc);
wr.BeginInvoke(this.outStream, null, null);
wr.BeginInvoke(this.errorStream, null, null);
}
public void start(string[] folders, string serverPath) {
this.inStream.WriteLine("chdir C:\\cygwin\\bin");
this.inStream.WriteLine("bash --login -i");
this.inStream.WriteLine("");
}
//code example from http://geekswithblogs.net/Waynerds/archive/2006/01/29/67506.aspx it is
//to make the textbox autoscroll I don't understand what it does, but it works.
#region autoscroll
[DllImport("User32.dll", CharSet = CharSet.Auto, EntryPoint = "SendMessage")]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
const int WM_VSCROLL = 277;
const int SB_BOTTOM = 7;
private void txtOut_TextChanged(object sender, EventArgs e) {
IntPtr ptrWparam = new IntPtr(SB_BOTTOM);
IntPtr ptrLparam = new IntPtr(0);
SendMessage(((RichTextBox)sender).Handle, WM_VSCROLL, ptrWparam, ptrLparam);
}
#endregion
private void ConsoleForm_FormClosed(object sender, FormClosedEventArgs e) {
this.stop = true;
try {
this.p.Kill();
} catch (InvalidOperationException) {
return;
}
}
另一个有趣的是,它并不总是像预期的那样隐藏 cmd 窗口。它第一次隐藏它,然后第二次(或之后)它不会隐藏它。此时用户可以关闭 cmd.exe 窗口并导致 readline 表现得很有趣。除非退出,否则它也永远不会读取输出到 cmd 的最后一行。
对于如何解决这个问题,有任何的建议吗?
解决方案
当你在你的代码中while(true)
循环你会以100%的盯住你的CPU(或至少一个核心),除非你也有办法跳出循环。在你的情况,你有一个return
声明,但在回路中的任何时候你有没有做任何的stop
变量守护着它。
其他提示
我会改变:
while(true)
为:
while(!sr.EOS) {
}
有一种更好的方法来检查结束循环。
这似乎是一个有趣的问题。乍一看,ReadLine 似乎存在一个问题,即在尝试读取数据时,句柄从其下方关闭,因此似乎是框架中的一个错误。然而,我不太相信这是 .Net 框架中的一个错误......
然而,这里存在一些低级问题。
到目前为止,您得到的其他答案都建议您修改 while 循环。我也会这样做,但我不认为这是你问题的根源。您不需要在那里睡眠,因为您将从 ReadLine() 获取等待状态,除非没有数据可读取,并且它只是返回失败,然后您将“紧密循环”。因此,请确保在此循环期间检查所有错误状态。
如果你不这样做,我就能看到问题。
如果其他一切都正常工作,那么如果我是你,我会首先尝试确定是否可以使用一个小型演示程序在程序之外复制它。我确信框架的流处理中有大量的错误检查。然而,看起来您正在从 Cygwin 运行一些东西,这就是您从 cmd shell 读取的输出。
尝试制作一个简单的应用程序,仅将数据吐出到标准输出和标准错误,然后确保应用程序在您仍在阅读时关闭。
还可以使用调试器查看发生故障后的哪行==。
拉里
具有while(true)
与环中没有睡眠将导致100%的CPU使用率。
您需要睡一定的时间或在某些时候跳出循环,这样CPU可以做其他的事情。
目前,你应该做的线沿线的东西最起码的:
while (sr.Peek() >= 0)
{
Console.WriteLine(sr.ReadLine());
Thread.Sleep(0);
}