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

发布于 2024-07-11 15:51:01 字数 2689 浏览 8 评论 0原文

我有这个方法:

    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 的最后一行。

对于如何解决这个问题,有任何的建议吗?

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). When the user closes the cmd.exe window, it causes the CPU usage to jump to 100%. When playing with the debugger I see that it stops on the sr.ReadLine() and never returns. Because this is watching both the StandardErrorStream and the StandardOutputStream it uses 100% on both cores.

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. 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. It also never reads the last line outputted to cmd unless it exits.

Any suggestions on how to fix this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

夜深人未静 2024-07-18 15:51:01

我会将: 更改

while(true)

为:

while(!sr.EOS) {

}

这是检查结束循环的更好方法。

I would change:

while(true)

to:

while(!sr.EOS) {

}

It is a better way to check to end the loop.

旧时光的容颜 2024-07-18 15:51:01

每当你的代码中有一个 while(true) 循环时,你就会将你的 cpu(或至少一个核心)固定在 100%,除非你还有办法跳出循环。 在您的情况下,您确实有一个 return 语句,但在循环中的任何时候您都不会对保护它的 stop 变量执行任何操作。

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. 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.

我是男神闪亮亮 2024-07-18 15:51:01

这似乎是一个有趣的问题。 乍一看,ReadLine 似乎存在问题,在尝试读取数据时,句柄从其下方关闭,因此似乎是框架中的错误。 但是,我不太容易相信这是 .Net 框架中的错误...

但是,这里存在一些低级问题。

到目前为止,您得到的其他答案都建议您修改 while 循环。 我也会这样做,但我不认为这是你问题的根源。 您不需要在那里睡眠,因为您将从 ReadLine() 获取等待状态,除非没有数据可读取,并且它只是返回失败,然后您将“紧密循环”。 因此,请确保在此循环期间检查所有错误状态。

如果你不这样做,我就能看到问题。

如果其他一切都正常工作,那么如果我是你,我会首先尝试确定是否可以使用一个小型演示程序在程序之外复制它。 我确信框架的流处理中有大量的错误检查。 然而,看起来您正在从 Cygwin 运行一些东西,这就是您从 cmd shell 读取的输出。

尝试制作一个简单的应用程序,仅将数据吐出到 stdout 和 stderr,然后确保应用程序在您仍在阅读时关闭。

还可以使用调试器查看失败发生后的行 == 。

拉里

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. However, I'm not convinced that easily that it's a bug in the .Net framework...

However, there are a couple low level issues here.

The other answers you have got so far all suggest you modify the while loop. 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'. 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. However, it looks like you are running some stuff from Cygwin, and that's the output you are reading from the 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..

Also use the debugger to see what line== after the failure occurs.

Larry

辞别 2024-07-18 15:51:01

在循环中使用 while(true) 且不进行睡眠将导致 CPU 使用率达到 100%。

您需要休眠一段时间或在某个时刻跳出循环,以便 CPU 可以执行其他操作。

至少你应该做一些类似的事情:

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

Having while(true) with no sleep in the loop will cause 100% CPU usage.

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.

At the very least you should be doing something along the lines of:

while (sr.Peek() >= 0) 
{
    Console.WriteLine(sr.ReadLine());
    Thread.Sleep(0);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文