java-процесс 'inputStream застрял - PullRequest
3 голосов
/ 26 января 2012

Вот мой сценарий:

  1. процесс A порождает дочерний процесс B и запускает потоки для вывода выходных данных B.
  2. процесс B порождает демон-процесс C и выводит его выходные данные.
  3. процесс B завершается, процесс-демон все еще живет.
  4. процесс A обнаруживает, что процесс B завершен через process.waitFor ().Однако он застревает при чтении входных потоков процесса B. Это потому, что B запустил демон.Входной поток получает EOF только при выходе из процесса C.

Это происходит только в Windows.Я использую ProcessBuilder.Вот решения, которые я придумал, и я хотел бы услышать ваши отзывы, поскольку ни одно из решений, которые мне действительно нравятся:

  1. Я могу использовать jna для запуска процесса демона C. Таким образом, яможет создать процесс, который «достаточно отделен», и процесс A не застрял при сливе потоков из B. Это работает, но я не очень заинтересован в этом решении, потому что это означает некоторый нативный код (и многое из того, что ястремится потреблять входы).Некоторое вдохновение, как это сделать с помощью JNA, можно найти здесь: http://yajsw.sourceforge.net (однако он содержит гораздо больше материала, чем просто запуск процесса).
  2. Запуск на jre7.Jdk7 привносит в ProcessBuilder несколько новых полезных вещей, например, метод LegitIO (), который тоже решает мою проблемуПо-видимому, когда унаследованный () включен, я могу просто закрыть все потоки в процессе демона C (что я делаю в любом случае, потому что это демон), и это решает проблему.Однако мне нужно запустить на jre5 +
  3. Закрыть System.out и System.err в процессе B до запуска процесса демона C. Опять же, это решает проблему, но мне действительно нужны те,Потоки для работы в процессе B, как я пишу полезные вещи для них.Не хорошо.Я надеялся, что смогу воспользоваться этой характеристикой, поместив какой-то процесс начальной загрузки между B & C, но это не решило проблему.
  4. У меня нет такой проблемы в Linux, поэтому я мог только запуститьна Linux?Нет, я не могу.
  5. Сделал процесс A неблокирующим выходом из процесса B.Это несколько работает, но это не удобно.Например, inputStream.read () не прерывается.Я мог бы использовать inputStream.available (), но он не различает EOF и доступные нулевые байты.Таким образом, решение подходит только в том случае, если процесс A никогда не интересуется EOF вывода B.Кроме того, это решение кажется более загруженным процессором и, как правило, выглядит неуклюже и не является пуленепробиваемым.
  6. Запустите процесс C в режиме --dry-run, где он просто проверяет, можно ли его запустить.Он пытается начать, отправляет приветственное сообщение и выходит.Он больше не работает, поэтому он не будет блокировать чтение.Процесс B может получить достаточную уверенность в том, что C может быть запущен, и мы можем использовать относительно простой код JNA для порождения отдельного процесса без использования его выходных данных (это приводит к тому, что связанный с JNA код делает грязный и тяжелый код, связанный с JNA).Единственная проблема заключается в том, что мы больше не потребляем выходные данные процесса C, но это можно решить, заставив C записать в хорошо известный файл, который может использовать процесс B.Это решение больше похоже на большой и уродливый обходной путь, но отчасти работает для нас.В любом случае, мы сейчас пробуем решение 1).

Буду очень признателен за любые подсказки!

1 Ответ

0 голосов
/ 15 августа 2012

Я только что столкнулся с той же проблемой.Я думаю, что у меня есть решение этой проблемы.В процессе A у меня есть следующий фрагмент кода после Process.waitFor (), где outT и errT являются потоками для чтения stdout и stderr процесса B, соответственно:

try {
  outT.join(1000);
  if (outT.isAlive()) {
    errmsg("stdout reader still alive, interrupting", null);
    outT.interrupt();
  }
} catch (Exception e) {
  errmsg("Exception caught from out stream reader: "+e, e);
}
try {
  errT.join(1000);
  if (errT.isAlive()) {
    errmsg("stderr reader still alive, interrupting", null);
    errT.interrupt();
  }
} catch (Exception e) {
  errmsg("Exception caught from err stream reader: "+e, e);
}
p.destroy();

Не уверен, что p.destroy (), но я пробовал все виды комбинаций для решения этой проблемы.

В любом случае, в методе run () потоков outT / errT у меня есть следующее, где 'pipe'переменная - это экземпляр Writer, в который я записываю stdout / stderr подпроцесса.Переменная 'in' - это поток stdout или stderr, полученный из Process:

try {
  r = new BufferedReader(new InputStreamReader(in, enc));
  String line;
  while (true) {
    if (Thread.currentThread().isInterrupted()) {
      errmsg("Text stream reader interrupted", null);
      break;
    }
    if (r.ready()) {
      line = r.readLine();
      if (line == null) {
        break;
      }
      pipe.write(line);
      pipe.write(SystemUtil.EOL);
      if (autoFlush) {
        pipe.flush();
      }
    }
  }
  pipe.flush();

} catch (Throwable t) {
  errmsg("Exception caught: "+t, t);
  try { pipe.flush(); } catch (Exception noop) {}

} finally {
  IOUtil.closeQuietly(in);
  IOUtil.closeQuietly(r);
}

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

...