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 的最后一行,除非它退出。

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

4

4 回答 4

9

我会改变:

while(true)

到:

while(!sr.EOS) {

}

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

于 2009-01-14T19:54:17.710 回答
7

每当您while(true)的代码中有一个循环时,您都会将您的 cpu(或至少一个核心)固定在 100%,除非您也有办法跳出循环。在您的情况下,您确实有一个return语句,但是在循环中,您从来没有对stop保护它的变量做任何事情。

于 2009-01-14T19:53:49.207 回答
1

while(true)循环中没有睡眠将导致 100% 的 CPU 使用率。

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

至少你应该按照以下方式做一些事情:

while (sr.Peek() >= 0) 
{
    Console.WriteLine(sr.ReadLine());
    Thread.Sleep(0);
}
于 2009-01-14T19:54:02.187 回答
1

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

但是,这里有几个低级问题。

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

如果你不这样做,我可以看到问题。

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

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

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

拉里

于 2009-01-14T20:05:04.270 回答