ループが100%CPUを使用し、終了しないのはなぜですか?
-
22-07-2019 - |
質問
このメソッドがあります:
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;
}
}
もう1つの興味深い点は、コマンドウィンドウが本来のように常に非表示になるとは限らないことです。最初に非表示にし、2番目(またはその後)に非表示にしません。これは、ユーザーがcmd.exeウィンドウを閉じて、readlineがおかしくなる場合です。また、終了しない限り、cmdに出力された最後の行を読み取りません。
これを修正する方法についての提案はありますか?
解決
コードに while(true)
ループがある場合は常に、CPU(または少なくとも1つのコア)を100%でペグします。ループの。あなたの場合、 return
ステートメントがありますが、ループ内のどの時点でも、それを保護する stop
変数に対して何かを行うことはありません。
他のヒント
変更します:
while(true)
to:
while(!sr.EOS) {
}
ループの終了を確認するより良い方法です。
これはここで興味深い問題のようです。一見すると、ReadLineには、データを読み取ろうとしているときにハンドルが下から閉じられるという問題があるように見えるため、フレームワークのバグのように見えます。ただし、それが.Netフレームワークのバグであると簡単に確信することはできません...
ただし、ここにはいくつかの低レベルの問題があります。
これまでに得た他の回答はすべて、whileループを変更することを示唆しています。私もこれを行いますが、これがあなたの問題の根本原因だとは思いません。 ReadLine()からwaitstateを取得するので、そこにスリープする必要はありません。読み取るデータがなく、失敗を返すだけの場合は、「タイトループ」になります。そのため、このループ中にすべてのエラー状態をチェックしていることを確認してください。
そうしない場合、問題が表示されます。
他のすべてが正常に機能している場合、私があなたなら、小さなデモプログラムであなたのプログラムの外でそれを複製できるかどうかを特定することから始めます。フレームワークのストリーム処理には多くのエラーチェックがあると確信しています。ただし、Cygwinから何かを実行しているように見え、それがcmdシェルから読み取っている出力です。
データをstdoutとstderrに出力するだけのシンプルなアプリを作成し、読みながらまだアプリを閉じるようにしてください。
また、デバッガーを使用して、障害発生後のline ==を確認します。
ラリー
ループ内でスリープせずに while(true)
を設定すると、CPU使用率が100%になります。
CPUが他の処理を行えるように、一定時間スリープするか、ある時点でループを抜ける必要があります。
少なくとも、次の行に沿って何かを行う必要があります。
while (sr.Peek() >= 0)
{
Console.WriteLine(sr.ReadLine());
Thread.Sleep(0);
}