5

プロセスを実行して、その入力、出力、およびエラーストリームを処理しようとしています。これを行うための明白な方法は、のようなものを使用することですが、それをselect()行うJavaで私が見つけることができる唯一のものはSelector.select()、を取りますChannel。またはChannelからを取得することはできないようです(メソッドは ありますが、ここでは役に立ちません)InputStreamOutputStreamFileStreamgetChannel()

そこで、代わりに、すべてのストリームをポーリングするコードを作成しました。

while( !out_eof || !err_eof )
{
    while( out_str.available() )
    {
        if( (bytes = out_str.read(buf)) != -1 )
        {
            // Do something with output stream
        }
        else
            out_eof = true;
    }
    while( err_str.available() )
    {
        if( (bytes = err_str.read(buf)) != -1 )
        {
            // Do something with error stream
        }
        else
            err_eof = true;
    }
    sleep(100);
}

終了しないことを除いて、これは機能します。ストリームの1つがファイルの終わりに達すると、available()ゼロを返すためread()、呼び出されず、EOFを示す-1の戻り値を取得することはありません。

1つの解決策は、EOFを検出するための非ブロッキング方法です。ドキュメントのどこにも表示されません。あるいは、私がやりたいことをするためのより良い方法はありますか?

私はここにこの質問を見ます: リンクテキスト とそれは私が望むものを正確に実行しませんが、私が今抱えている特定の問題のために、ストリームごとに別々のスレッドを生成するというそのアイデアをおそらく使用できます。しかし、確かにそれがそれを行う唯一の方法ではありませんか?確かに、それぞれにスレッドを使用せずに複数のストリームから読み取る方法が必要ですか?

4

2 に答える 2

4

あなたが言ったように、この回答で概説されている解決策は、プロセスから stdout と stderr の両方を読み取る従来の方法です。少し面倒ではありますが、ストリームごとのスレッドが最適です。

于 2008-09-24T10:16:41.193 に答える
2

実際、監視したいストリームごとにスレッドを生成するルートをたどる必要があります。ユース ケースで問題のプロセスの stdout と stderr の両方を組み合わせることができる場合は、必要なスレッドは 1 つだけですが、それ以外の場合は 2 つ必要です。

外部プロセスを起動し、その出力を取得して何かを実行すると同時に、エラーとプロセスの終了を探し、それを終了する必要がある私たちのプロジェクトの 1 つで、それを正しく理解するのにかなりの時間がかかりましたJava アプリのユーザーが操作をキャンセルしたとき。

run() メソッドが次のように見える監視部分をカプセル化するために、かなり単純なクラスを作成しました。

public void run() {
    BufferedReader tStreamReader = null;
    try {
        while (externalCommand == null && !shouldHalt) {
            logger.warning("ExtProcMonitor("
                           + (watchStdErr ? "err" : "out")
                           + ") Sleeping until external command is found");
            Thread.sleep(500);
        }
        if (externalCommand == null) {
            return;
        }
        tStreamReader =
                new BufferedReader(new InputStreamReader(watchStdErr ? externalCommand.getErrorStream()
                        : externalCommand.getInputStream()));
        String tLine;
        while ((tLine = tStreamReader.readLine()) != null) {
            logger.severe(tLine);
            if (filter != null) {
                if (filter.matches(tLine)) {
                    informFilterListeners(tLine);
                    return;
                }
            }
        }
    } catch (IOException e) {
        logger.logExceptionMessage(e, "IOException stderr");
    } catch (InterruptedException e) {
        logger.logExceptionMessage(e, "InterruptedException waiting for external process");
    } finally {
        if (tStreamReader != null) {
            try {
                tStreamReader.close();
            } catch (IOException e) {
                // ignore
            }
        }
    }
}

呼び出し側では、次のようになります。

    Thread tExtMonitorThread = new Thread(new Runnable() {

        public void run() {
            try {
                while (externalCommand == null) {
                    getLogger().warning("Monitor: Sleeping until external command is found");
                    Thread.sleep(500);
                    if (isStopRequested()) {
                        getLogger()
                                .warning("Terminating external process on user request");
                        if (externalCommand != null) {
                            externalCommand.destroy();
                        }
                        return;
                    }
                }
                int tReturnCode = externalCommand.waitFor();
                getLogger().warning("External command exited with code " + tReturnCode);
            } catch (InterruptedException e) {
                getLogger().logExceptionMessage(e, "Interrupted while waiting for external command to exit");
            }
        }
    }, "ExtCommandWaiter");

    ExternalProcessOutputHandlerThread tExtErrThread =
            new ExternalProcessOutputHandlerThread("ExtCommandStdErr", getLogger(), true);
    ExternalProcessOutputHandlerThread tExtOutThread =
            new ExternalProcessOutputHandlerThread("ExtCommandStdOut", getLogger(), true);
    tExtMonitorThread.start();
    tExtOutThread.start();
    tExtErrThread.start();
    tExtErrThread.setFilter(new FilterFunctor() {

        public boolean matches(Object o) {
            String tLine = (String)o;
            return tLine.indexOf("Error") > -1;
        }
    });

    FilterListener tListener = new FilterListener() {
        private boolean abortFlag = false;

        public boolean shouldAbort() {
            return abortFlag;
        }

        public void matched(String aLine) {
            abortFlag = abortFlag || (aLine.indexOf("Error") > -1);
        }

    };

    tExtErrThread.addFilterListener(tListener);
    externalCommand = new ProcessBuilder(aCommand).start();
    tExtErrThread.setProcess(externalCommand);
    try {
        tExtMonitorThread.join();
        tExtErrThread.join();
        tExtOutThread.join();
    } catch (InterruptedException e) {
        // when this happens try to bring the external process down 
        getLogger().severe("Aborted because auf InterruptedException.");
        getLogger().severe("Killing external command...");
        externalCommand.destroy();
        getLogger().severe("External command killed.");
        externalCommand = null;
        return -42;
    }
    int tRetVal = tListener.shouldAbort() ? -44 : externalCommand.exitValue();

    externalCommand = null;
    try {
        getLogger().warning("command exit code: " + tRetVal);
    } catch (IllegalThreadStateException ex) {
        getLogger().warning("command exit code: unknown");
    }
    return tRetVal;

残念ながら、自己完結型の実行可能な例を作成する必要はありませんが、おそらくこれが役立つでしょう。もう一度やり直さなければならない場合は、自作の停止フラグの代わりに Thread.interrupt() メソッドを使用する方法をもう一度検討します (揮発性と宣言することを忘れないでください!) が、それは別の機会にします。:)

于 2008-09-24T10:37:01.630 に答える