Почему мой цикл использует 100% ресурсов ЦП и никогда не заканчивается?

StackOverflow https://stackoverflow.com/questions/444401

Вопрос

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

    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 и заставить строку чтения вести себя странно.Он также никогда не читает последнюю строку, выведенную в cmd, пока не завершится.

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

Это было полезно?

Решение

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

Другие советы

Я бы поменял:

while(true)

к:

while(!sr.EOS) {

}

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

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

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

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

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

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

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

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

Ларри

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

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

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

while (sr.Peek() >= 0) 
{
    Console.WriteLine(sr.ReadLine());
    Thread.Sleep(0);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top