Process.waitFor (), потоки и InputStreams - PullRequest
32 голосов
/ 28 января 2010

В псевдокоде вот что я делаю:

Process proc = runtime.exec(command);
processOutputStreamInThread(proc.getInputStream());
processOutputStreamInThread(proc.getErrorStream());
proc.waitFor()

Однако иногда processOutputStreamInThread не видит никакого вывода, а иногда и делает. Грубо говоря, метод создает BufferedInputStream выходных данных команды и отправляет их в логгер.

Исходя из того, что я вижу, я предполагаю, что command не нужно, чтобы весь вывод выводился в потоки, подаваемые getInputStream() и getErrorStream(), что позволяет потоку быть пустым.

Результатами моих испытаний являются следующие вопросы:

(1) Требует ли waitFor() in java.lang.Process , чтобы вывод выполненной программы был прочитан до того, как он вернется?

В документации указано только:

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

(2) При каких условиях необходимо закрывать потоки, предоставленные getInputStream и getErrorStream, и / или они закрываются автоматически?

В документации указано только:

Получает поток ошибок подпроцесса. Поток получает данные из потока вывода ошибок процесса, представленного этим объектом Process.

Замечание по реализации: рекомендуется буферизовать входной поток.

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

Редактировать: изменено getOutputStream на getInputStream, теперь присутствует выше.

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

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

Мой окончательный код выглядел примерно так:

ProcessBuilder pb = new ProcessBuilder(cmdargs);
pb.redirectErrorStream(true);
Process proc = pb.start();
processOutputStream(proc.getInputStream());
proc.waitFor()

Ответы [ 2 ]

29 голосов
/ 28 января 2010

Если ваш внешний процесс ожидает что-то от stdin, вы ДОЛЖНЫ закрыть getOutputStream. В противном случае вы будете waitFor навсегда.

Вот , когда Runtime.exec () не публикует статью из JavaWorld, которая описывает различные подводные камни метода exec и как их избежать.

Исходя из моего опыта, лучше использовать STDOUT и STDERR дочернего процесса (до тех пор, пока они не завершатся), а затем заблокировать waitFor. Надеюсь, в этот момент вам не придется долго ждать.

Ответ на вопрос Калеба. В нормальных условиях вы не должны закрывать потоки, однако, поскольку вы waitingFor и по какой-либо причине у него нет тайм-аута, вам может потребоваться закрыть эти потоки, если вы столкнетесь с некоторыми ошибочными условиями в выходных данных и не хотите обрабатывать вывод ребенка дальше. Однако, будет ли дочерняя программа завершена (аварийно завершена), когда ее канал STDOUT или STDERR закрыт на другом конце, полностью зависит от реализации этого дочернего элемента. Тем не менее, большинство программ оболочки будут закрываться при таких условиях.

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

2 голосов
/ 28 января 2010

Я думаю, что это немного нелогично, но:

getOutputStream Получает вывод Поток подпроцесса. Выход на поток поступает в стандарт входной поток процесса представлен этим объектом процесса. Примечание по реализации: это хорошая идея для выходного потока, который будет буферизован. Возвращает: выходной поток подключен к нормальному входу подпроцесса.

Я прочитал это как этот поток вывода из основного процесса и присоединен к стандартному входу подпроцесса, поэтому, когда вы пишете в getOutputStream (). Write (), вы фактически пишете на стандартный ввод.

Возможно, вы хотите использовать .getInputStream ()?

Возвращает: входной поток подключен к обычному выходу подпроцесса.

Что касается Process.Waitfor (), документация API гласит:

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

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

...