【发布时间】:2018-09-24 08:05:24
【问题描述】:
我有一个 Java 程序,它启动一个由 Process 类表示的单独子进程,然后附加监听器来查看 Process 的 stdout/stderr。在某些情况下,进程会挂起并停止运行,此时 TimeLimiter 将抛出 TimeoutException,尝试中断实际上正在执行 readLine() 调用的底层线程,然后使用 @987654326 终止进程@ 并关闭来自 Process 对象的 stdout 和 stderr 流。它尝试做的最后一件事是关闭 BufferedReader,但这个调用永远挂起。示例代码如下:
private static final TimeLimiter timeLimiter = new SimpleTimeLimiter(); // has its own thread pool
public void readStdout(Process process) {
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
try {
String line = null;
while ((line = timeLimiter.callWithTimeout(reader::readLine, 5, TimeUnit.SECONDS, true)) != null) { // this will throw a TimeoutException when the process hangs
System.out.println(line);
}
} finally {
killProcess(process); // this does a "kill -9" on the process
process.getInputStream().close(); // this works fine
process.getErrorStream().close(); // this works fine
reader.close(); // THIS HANGS FOREVER
}
}
为什么close() 调用会永远挂起,我该怎么办?
相关问题:Program freezes on bufferedreader close
更新:
如果不清楚,TimeLimiter 来自 Guava 库:https://github.com/google/guava/blob/master/guava/src/com/google/common/util/concurrent/SimpleTimeLimiter.java
另外,我被要求提供 killProcess() 方法的代码,所以这里是(注意这仅适用于 Linux/Unix 机器):
public void killProcess(Process process) {
// get the process ID (pid)
Field field = process.getClass().getDeclaredField("pid"); // assumes this is a java.lang.UNIXProcess
field.setAccessible(true);
int pid = (Integer)field.get(process);
// populate the list of child processes
List<Integer> processes = new ArrayList<>(Arrays.asList(pid));
for (int i = 0; i < processes.size(); ++i) {
Process findChildren = Runtime.getRuntime().exec(new String[] { "ps", "-o", "pid", "--no-headers", "--ppid", Integer.toString(processes.get(i)) });
findChildren.waitFor(); // this will return a non-zero exit code when no child processes are found
Scanner in = new Scanner(findChildren.getInputStream());
while (in.hasNext()) {
processes.add(in.nextInt());
}
in.close();
}
// kill all the processes, starting with the children, up to the main process
for (int i = processes.size() - 1; i >= 0; --i) {
Process killProcess = Runtime.getRuntime().exec(new String[] { "kill", "-9", Integer.toString(processes.get(i)) });
killProcess.waitFor();
}
}
【问题讨论】:
-
你是如何杀死进程的? Process#destroy() 和它的兄弟destroyForcibly 似乎也清理了流。在类似的情况下,人们有更多的success。
-
好吧,我试图在帖子中简化这一点,但如果您想了解更多细节,我们正在运行的进程实际上是 ffmpeg,而 ffmpeg 会衍生出各种子进程。当ffmpeg挂起时,我们最初只是按照你说的尝试了destroy()和destroyForcibly(),但事实证明这些方法并没有破坏子进程,所以我们不得不编写一个使用“ps”列出PID和子进程的子程序,然后遍历并在进程树中的每个进程和子进程上执行“kill -9”。
标签: java multithreading memory-leaks bufferedreader freeze