Java: закрытие потоков подпроцесса STD? - PullRequest
3 голосов
/ 19 апреля 2011

из javadoc для java.lang.Process:

Методы, которые создают процессы, могут не работать должным образом для специальных процессов на определенных собственных платформах, таких как собственные процессы управления окнами, процессы-демоны, процессы Win16 / DOS в Microsoft Windows или сценарии оболочки. Созданный подпроцесс не имеет собственного терминала или консоли. Все его стандартные операции io (т.е. stdin, stdout, stderr) будут перенаправлены на родительский процесс через три потока (getOutputStream (), getInputStream (), getErrorStream ()). Родительский процесс использует эти потоки для подачи входных данных и получения выходных данных из подпроцесса. Поскольку некоторые собственные платформы предоставляют ограниченный размер буфера только для стандартных входных и выходных потоков, невозможность оперативной записи входного потока или чтения выходного потока подпроцесса может привести к блокировке подпроцесса и даже к взаимной блокировке.

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

Но что, если вы просто хотите запустить процесс и не заботиться о вводе / выводе? В Java есть способ освободить ресурсы в родительском процессе, которые предназначены для управления подпроцессом? (например, каналы ввода / вывода и ожидание состояния выхода подпроцесса)

Если я выполняю следующее в родительском процессе:

  Process.getOutputStream().close();
  Process.getInputStream().close();
  Process.getErrorStream().close();

мне все еще нужно беспокоиться о тупике? (например, если подпроцесс непрерывно отправляет данные на свой стандартный вывод)

1 Ответ

2 голосов
/ 19 апреля 2011

Здесь вам есть о чем беспокоиться:

  1. Будет ли процесс блокироваться, пытаясь записать на стандартный вывод, когда вы не сможете его использовать, и
  2. Как процесс будет интерпретировать закрывающиеся потоки?

Закрытие выходного потока явно не приведет к взаимоблокировке, если процесс не требует ввода. Однако процесс будет обнаруживать, что конец потока достигнут, и может соответствующим образом завершиться. Возьмите grep для примера. Эта программа завершит работу, когда вы закроете свой поток ввода.

Самый простой и безопасный способ игнорировать ввод - создать библиотеку, которая потребляет и отбрасывает ввод в отдельном потоке. Вы, наверное, видели класс StreamGobbler, который делает именно это.

Редактировать

Итак, я попробовал следующее с двумя разными программами подпроцесса:

Process proc = new ProcessBuilder(appStr).start();
proc.getInputStream().close();

InputStream eos = proc.getErrorStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(eos));
for ( String line = null; (line = reader.readLine()) != null; ) {
   System.err.println(line);
}

System.out.println(proc.waitFor());

В первый раз я вызвал простое Java-приложение:

public class App {
   public static void main(String[] args) throws Exception {
      for ( int i = 0; i < 1000; i++ ) {
         System.out.println(i);
      }
   }
}

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

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

Так что на самом деле то, что вы предлагаете при закрытии потоков, не будет успешным в общем случае. Вы должны знать, что дочерний процесс может корректно обрабатывать потерю своего выходного потока, и я подозреваю, что большинство приложений не будет. Моя интуиция заключается в том, что Java может совершать некоторые изящные сбои, когда выходной поток закрыт, так что с точки зрения программы поток все еще открыт, и никто его не слушает.

...