简体   繁体   English

为什么我的循环使用100%CPU且永不结束?

[英]Why does my loop use 100% CPU and never end?

I have this method: 我有这种方法:

    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();
            }
        }
    }

And it reads the streams from a Process (cmd.exe). 然后从进程(cmd.exe)中读取流。 When the user closes the cmd.exe window, it causes the CPU usage to jump to 100%. 当用户关闭cmd.exe窗口时,它将导致CPU使用率跃升至100%。 When playing with the debugger I see that it stops on the sr.ReadLine() and never returns. 当与调试器一起玩时,我看到它在sr.ReadLine()上停止并且永不返回。 Because this is watching both the StandardErrorStream and the StandardOutputStream it uses 100% on both cores. 因为这同时监视StandardErrorStream和StandardOutputStream,所以在两个内核上都使用100%。

Here's some more code of the project if you need it. 如果需要,这里还有一些项目代码。

    [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;
        }
    }

Another interesting this is that it doesn't always hide the cmd window like it's supposed to. 另一个有趣的是,它并不总是像预期的那样隐藏cmd窗口。 It hides it the first time, and then the second (or after) it won't hide it. 第一次将其隐藏,然后第二次(或之后)将不会隐藏它。 This is when the user can close the cmd.exe window and cause the readline to act funny. 这是用户可以关闭cmd.exe窗口并导致readline变得有趣的时候。 It also never reads the last line outputted to cmd unless it exits. 除非退出,否则它也永远不会读取输出到cmd的最后一行。

Any suggestions on how to fix this? 对于如何解决这个问题,有任何的建议吗?

I would change: 我会改变:

while(true)

to: 至:

while(!sr.EOS) {

}

It is a better way to check to end the loop. 这是检查结束循环的更好方法。

Whenever you have a while(true) loop in your code you're going to peg your cpu (or at least one core) at 100%, unless you also have a way to break out of the loop. 每当代码中有一个while(true)循环时,您都将100%固定cpu(或至少一个内核),除非您也有办法摆脱这种循环。 In your case, you do have a return statement, but at no point in the loop do you ever do anything to the stop variable guarding it. 就您而言,您确实有一个return语句,但是在循环中的任何时候,您都不会对保护它的stop变量做任何事情。

Having while(true) with no sleep in the loop will cause 100% CPU usage. 循环中没有休眠的while(true)将导致100%的CPU使用率。

You need to sleep for some amount of time or break out of the loop at some point so the CPU can do something else. 您需要睡眠一段时间或在某个时候脱离循环,以便CPU可以执行其他操作。

At the very least you should be doing something along the lines of: 至少您应该按照以下方式进行操作:

while (sr.Peek() >= 0) 
{
    Console.WriteLine(sr.ReadLine());
    Thread.Sleep(0);
}

This seems like an interesting issue here. 这似乎是一个有趣的问题。 At first glance, it would appear that ReadLine has an issue with the handle being closed from under it while it's trying to read data, and thus would seem to be a bug in the Framework. 乍一看,ReadLine似乎有问题,在尝试读取数据时手柄从其下面关闭,因此似乎是Framework中的错误。 However, I'm not convinced that easily that it's a bug in the .Net framework... 但是,我不敢轻易相信这是.Net框架中的错误...

However, there are a couple low level issues here. 但是,这里有几个低级问题。

The other answers you have got so far all suggest you modify the while loop. 到目前为止,您得到的其他答案都建议您修改while循环。 I would do this as well, but I don't think this is the root of your problem. 我也会这样做,但我认为这不是您问题的根源。 You do not need a sleep in there, because you will get your waitstate from the ReadLine(), unless there is no data to read, and it just returns a failue, THEN you will 'tight-loop'. 您不需要在那里睡觉,因为您将从ReadLine()获取等待状态,除非没有要读取的数据,并且它仅返回故障,然后您将“紧缩循环”。 So, make sure you are checking any and all error states during this loop. 因此,请确保在此循环期间检查所有错误状态。

If you do not, I can see issues. 如果您不这样做,我会看到问题。

If everything else is working as it should, then if I were you, I would start by trying to identify if you can duplicate it outside of your program with a small demo program. 如果其他所有功能都能正常工作,那么如果我是您,我将首先尝试确定是否可以使用小型演示程序在程序外复制它。 I'm sure there is plenty of error checking in the Framework's Stream handling. 我确信在Framework的Stream处理中有很多错误检查。 However, it looks like you are running some stuff from Cygwin, and that's the output you are reading from the cmd shell. 但是,看起来您正在从Cygwin运行某些东西,这就是您从cmd shell读取的输出。

Try making a simple app that just spits out data to stdout, and stderr, and then make sure the app closes while you are still reading.. 尝试制作一个简单的应用程序,该应用程序仅将数据输出到stdout和stderr,然后确保在您仍在阅读时关闭该应用程序。

Also use the debugger to see what line== after the failure occurs. 还可以使用调试器查看发生故障后的行==。

Larry 拉里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM