5

コンソール アプリケーションを実行する C# アプリケーション内でプロセスを開始しています。標準入力と標準出力をリダイレクトし、StandardOutput.ReadLine() を介して数行を読み取ることができます。ProcessStartInfo が正しく構成されていると確信しています。

コンソール アプリケーションは、開始されると、数行 (「マーカー」行で終わる) を出力し、入力を待ちます。入力を受け取った後、再び数行を出力します (再び「マーカー」行で終了します)。私の意図は、適​​切な入力文字列を送信することがわかっている「マーカー」行を受け取るまで、そこから行を読み取ることです。

私の問題は、何度か繰り返した後、プログラムがハングすることです。デバッガーを一時停止すると、StandardOutput.EndOfStream の呼び出し内でハングが発生する傾向があります。これは、次のテスト コードの場合です。

while (!mProcess.StandardOutput.EndOfStream) // Program hangs here.
{
    Console.WriteLine(mProcess.StandardOutput.ReadLine());
}

「マーカー」行をテストしているときに、行を読み取った後に StandardOutput.EndOfStream にアクセスしようとすると、同じ種類のハングが発生します。

string line = "";
while (!isMarker(line))
{
    line = mProcess.StandardOutput.ReadLine();
}
bool eos = mProcess.StandardOutput.EndOfStream; // Program hangs here.

このプロパティが非常にひどく機能する原因は何ですか?

4

4 に答える 4

10

ここで EndOfStream を確実に使用することはできません。StreamReader.EndOfStream プロパティは、バッファリングされた文字がない場合、StandardOutput.Read() を呼び出します。プロセスが出力パイプに何も送信しておらず、それを閉じていない場合、その Read() 呼び出しはブロックされます。入力を待っているため、これはほぼ確実に発生します。EndOfStream は、プロセスが出力パイプの終わりを閉じ、StreamReader がバッファリングされたすべての文字を消費するまで true を返しません。プログラム終了時。

「マーカー」行を検出するには、BeginOutputReadLine() を使用する方がよい場合があります。コールバックが別のスレッドで発生することに注意してください。また、プロセスがマーカーを送信するのを待つ必要はないことに注意してください。プロセスがそれを読み取る準備ができるまで、書き込みはすべてバッファリングされます。バッファーが小さいことに注意してください。デッドロックが発生する可能性があります。

于 2010-05-04T17:42:17.147 に答える
1

プロセス クラスと対話するときにデッドロックを作成できるパスは多数あります。Microsoft は、こちらの MSDN サイトでそれらについて説明しています。これが私がそれを呼び出す方法です。ErrorDataReceived と OutputDataReceived の処理と、BeginErrorReadLine と BeginOutputReadLine の呼び出しに注目してください。これにより、親プロセスがストリームを非同期的に読み取ることで、デッドロック シナリオが解消されます。注: RunProcessResponse は、私自身の小さなラッパー データ転送オブジェクトです。

Public Function RunProcess(ByVal executableFileName As String, ByVal arguments As String, ByVal workingDirectory As System.String) As RunProcessResponse
    Dim process As System.Diagnostics.Process = Nothing
    Dim response As RunProcessResponse

    Try
        process = New System.Diagnostics.Process()
        Dim psInfo As New System.Diagnostics.ProcessStartInfo()
        Dim errorString As System.String = String.Empty
        Dim outputString As System.String = String.Empty


        If Not System.String.IsNullOrEmpty(workingDirectory) Then
            psInfo.WorkingDirectory = workingDirectory
        End If

        psInfo.FileName = executableFileName
        psInfo.Arguments = arguments
        psInfo.WindowStyle = ProcessWindowStyle.Hidden
        psInfo.CreateNoWindow = True
        psInfo.RedirectStandardError = True
        psInfo.RedirectStandardOutput = True
        psInfo.UseShellExecute = False

        AddHandler process.ErrorDataReceived, Sub(sender As Object, args As DataReceivedEventArgs)
                                                  If args.Data IsNot Nothing Then
                                                      errorString &= args.Data & vbCrLf
                                                  End If
                                              End Sub
        AddHandler process.OutputDataReceived, Sub(sender As Object, args As DataReceivedEventArgs)
                                                   If args.Data IsNot Nothing Then
                                                       outputString &= args.Data & vbCrLf
                                                   End If
                                               End Sub

        process.StartInfo = psInfo
        process.Start()

        process.BeginErrorReadLine()
        process.BeginOutputReadLine()
        process.WaitForExit()

        response = New RunProcessResponse(errorString, outputString, process.ExitCode)

        Return response
    Finally
        If process IsNot Nothing Then
            process.Dispose()
        End If
    End Try
End Function
于 2011-02-18T15:09:06.930 に答える
0

標準出力から読み取る前に、プロセスが終了するのを待ちましたか:

mProcess.WaitForExit();
于 2010-05-04T17:29:01.360 に答える