Чтение входных потоков и потоков ошибок одновременно с использованием BufferedReaders зависает - PullRequest
5 голосов
/ 29 августа 2009

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

В моей программе есть раздел, который требует извлечения текста из потока ввода и потока ошибок из процесса, который запускается с помощью Runtime.getrunTime (). Exec () и передает его на стандартный ввод и вывод упорядоченным образом. манера. У меня есть функция, которая, насколько я могу судить, должна работать. Но кажется, что он попадает в ловушку 22, где он ожидает, когда поток сообщит о готовности, но поток завершен и не сообщает. Я сбит с толку. Я не могу придумать другой способ сделать это, который соответствует моим ограничениям, и я довольно скептически отношусь к тому, что такая ловушка-22 может существовать.

Вот мой код:

private void forwardStreamtoStd(InputStream in, InputStream err) 
throws IOException {
    int c = -1;
    BufferedReader inReader = new BufferedReader(
        new InputStreamReader(in, "US-ASCII"));
    BufferedReader errReader = new BufferedReader(
        new InputStreamReader(err, "US-ASCII"));
    boolean inFinished = false, errFinished = false;

    try {
        System.out.println("Begin stream read loop...");
        while (!inFinished && !errFinished) {
        if (!inFinished) {
            while (inReader.ready()) {
                if ((c = inReader.read()) == -1) {
                    inFinished = true;
                } 
                else {
                    System.out.print((char) c);
                }
            }
        }

        if (!errFinished) {
            while (errReader.ready()) {
                if ((c = errReader.read()) == -1) {
                    errFinished = true;
                } 
                else {
                     System.err.print((char) c);
                }
            }
        }
        }
        System.out.println("End stream read loop.");
    } 
    catch (IOException e) {
        throw e;
    } 
    finally {
        errReader.close();
        inReader.close();
    }
}

Проблема, похоже, заключается в том, что циклы чтения ждут, когда потоки сообщат о готовности, и в результате не видят возвращенное -1 при чтении, сообщая им, что пора выходить. Я пытаюсь избежать блокирования потоков, чтобы я мог вытащить их по очереди, когда они будут готовы. Тем не менее, как я могу поймать конец потока процесса? Я что-то пропустил? Не следует ли прочитать отчет о том, что он прочитан, когда у него есть конец потока -1? Процессы заканчиваются, и поэтому их потоки должны умирать. Что я тут не так делаю?

Ответы [ 4 ]

8 голосов
/ 29 августа 2009

Есть еще две возможности:

  • Используйте ProcessBuilder и вызовите redirectErrorStream(true), чтобы объединить два потока, и вам нужно прочитать один поток. У меня есть пример здесь .
  • В JDK7 вы можете вызвать inheritIO() для автоматической пересылки всего

Редактировать Во втором предположении кажется, что вызов ready () вводит в заблуждение вашу программу. Попробуйте это:

private void forwardStreamtoStd(InputStream in, InputStream err) 
throws IOException {
    int c = -1;
    BufferedReader inReader = new BufferedReader(
        new InputStreamReader(in, "US-ASCII"));
    BufferedReader errReader = new BufferedReader(
        new InputStreamReader(err, "US-ASCII"));
    boolean inFinished = false, errFinished = false;

    try {
        System.out.println("Begin stream read loop...");
        if (!inFinished) {
            while ((c = inReader.read()) != -1) {
                    System.out.print((char) c);
            }
            inFinished = true;
        }

        if (!errFinished) {
            while ((c = errReader.read()) != -1) {
                System.err.print((char) c);
            }
            errFinished = true;
        }
        System.out.println("End stream read loop.");
    } 
    catch (IOException e) {
        throw e;
    } 
    finally {
        errReader.close();
        inReader.close();
    }
}

Или еще лучше, отключите BufferedReader, если вы не планируете никаких дополнительных преобразований:

private void createReader(final InputStream in, final OutputStream out) {
    new Thread() {
        public void run() {
            try {
                int c = 0;
                while ((c = in.read()) != -1) {
                    out.write(c);
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            } finally {
                in.close();
            }
        }
    }.start();
}
private void forwardStreamtoStd(InputStream in, InputStream err) 
throws IOException {
    createReader(in, System.out);
    createReader(err, System.err);
}
5 голосов
/ 29 августа 2009

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4090471

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

3 голосов
/ 29 августа 2009

необходимо использовать два потока одновременно, чтобы предотвратить блокировку. См. эту статью для получения дополнительной информации и, в частности, обратите внимание на механизм StreamGobbler, который захватывает stdout / err в отдельных потоках.

1 голос
/ 29 августа 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...