Почему мой цикл использует 100% CPU и никогда не заканчивается? - PullRequest
0 голосов
/ 14 января 2009

У меня есть этот метод:

    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, загрузка ЦП переходит на 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, если не завершается.

Есть предложения, как это исправить?

Ответы [ 4 ]

9 голосов
/ 14 января 2009

Я бы изменил:

while(true)

до:

while(!sr.EOS) {

}

Это лучший способ проверить конец цикла.

7 голосов
/ 14 января 2009

Всякий раз, когда в вашем коде есть цикл while(true), вы собираетесь привязать свой процессор (или, по крайней мере, одно ядро) к 100%, если у вас также нет способа выйти из цикла. В вашем случае у вас есть оператор return, но вы нигде в цикле не делаете что-либо с переменной stop, которая его защищает.

1 голос
/ 14 января 2009

Это кажется интересным вопросом здесь. На первый взгляд может показаться, что у ReadLine есть проблема с закрывающимся из-под него дескриптором, когда он пытается прочитать данные, и, таким образом, это может показаться ошибкой в ​​Framework. Тем не менее, я не уверен, что это просто ошибка в .Net Framework ...

Однако здесь есть пара проблем низкого уровня.

Все остальные ответы, которые вы получили до сих пор, предлагают изменить цикл while. Я бы тоже так поступил, но не думаю, что это корень вашей проблемы. Вам там не нужен сон, потому что вы получите состояние ожидания из ReadLine (), если нет данных для чтения, и он просто возвращает ошибку, тогда вы будете «замкнуты». Поэтому убедитесь, что вы проверяете все состояния ошибок во время этого цикла.

Если нет, я вижу проблемы.

Если бы все остальное работало так, как должно, то на вашем месте я бы начал с того, чтобы попытаться определить, можете ли вы скопировать его вне вашей программы с помощью небольшой демонстрационной программы. Я уверен, что есть много проверки ошибок в обработке потока Framework. Тем не менее, похоже, что вы запускаете что-то из Cygwin, и это вывод, который вы читаете из оболочки cmd.

Попробуйте создать простое приложение, которое просто выплевывает данные в stdout и stderr, а затем убедитесь, что приложение закрывается, пока вы все еще читаете ..

Также используйте отладчик, чтобы увидеть, какая строка == после сбоя.

Larry

1 голос
/ 14 января 2009

Наличие while(true) без ожидания в цикле приведет к 100% загрузке ЦП.

Вам нужно поспать некоторое время или выйти из цикла в какой-то момент, чтобы процессор мог сделать что-то еще.

По крайней мере, вы должны делать что-то вроде:

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