Process.waitFor()、threads 和 InputStreams

2022-09-01 09:01:37

在伪代码中,我正在做的是:

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

但是,有时看不到任何输出,有时可以看到。粗略地说,该方法创建命令的输出并将其发送到记录器。processOutputStreamInThreadBufferedInputStream

根据我所看到的,我猜不需要将其所有输出转储到由 和 馈送的流中,从而允许流为空。commandgetInputStream()getErrorStream()

我的试验结果如下:

(1)java.lang.Process 中,是否要求在执行程序返回之前已读取其输出?waitFor()

文档仅说明:

如有必要,使当前线程等待,直到此对象所表示的进程终止。如果子进程已终止,则此方法将立即返回。如果子进程尚未终止,则调用线程将被阻塞,直到子进程退出。Process

(2) 在什么情况下,溪流由提供并需要关闭和/或自动关闭?getInputStreamgetErrorStream

文档仅说明:

获取子进程的错误流。该流从此 Process 对象所表示的进程的错误输出流中获取管道传输的数据。

实现说明:最好对输入流进行缓冲。

一位用户报告说,他必须自己关闭流,但我至少部分时间会遇到异常,表明当我尝试这样做时,流已经关闭。

编辑:更改为 ,现在在上面显示。getOutputStreamgetInputStream

分辨率:问题最终是,在某些情况下,用于处理输出流的线程直到我非常短暂的进程完成后才会运行,导致输入流没有给我任何数据。 没有执行任何等待执行程序的输出。相反,程序在收集任何输出之前运行并终止。waitFor

我使用线程是因为我不确定我将在标准误差和标准输出上获得多少输出,并且我希望能够同时处理两者,而不会阻塞其中一个或另一个,如果其中只有一个具有可用数据。但是,由于我的线程无法始终如一地读取已执行程序的输出,因此这不是解决方案。

我的最终编码看起来像这样:

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

答案 1

如果外部进程需要在其 上显示某些内容,则必须关闭 .否则你将永远。stdingetOutputStreamwaitFor

这是来自JavaWorld的当运行时.exec()不会的文章,它描述了exec方法的不同陷阱以及如何避免它们。

根据我的经验,最好使用子进程的STDOUT和STDERR(直到它们EOF),然后阻止。希望在这一点上,您不必等待很长时间。waitFor

Kaleb问题的答案。在正常情况下,您不应该关闭流,但是由于您并且由于某种原因它没有超时,因此,如果您在输出中遇到某些错误情况并且不想进一步处理子级的输出,则可能需要关闭这些流。但是,当子程序在另一端关闭 STDOUT 或 STDERR 管道时,子程序是否会终止(崩溃)完全取决于该子程序的实现。但是,大多数shell程序都会在这种情况下终止。waitingFor

我真的希望有一些有意义的超时,当你决定放弃监控时,有一个记录的方法来清理其资源。waitForProcess


答案 2

我认为这有点违反直觉,但是:

getOutputStream 获取子进程的输出流。流的输出通过管道传输到此 Process 对象所表示的进程的标准输入流中。实现说明:最好对输出流进行缓冲。返回:连接到子进程的正常输入的输出流。

我读到这个输出流来自主进程,并附加到子进程的标准输入,所以当你写到getOutputStream().write()时,你实际上是在stdin上写。

您是否可能要使用 .getInputStream()?

返回:连接到子进程的正常输出的输入流。

至于Process.Waitfor(),API文档说:

如有必要,使当前线程等待,直到此 Process 对象所表示的进程终止。如果子进程已终止,则此方法将立即返回。如果子进程尚未终止,则调用线程将被阻塞,直到子进程退出。

我会说,在进程完成执行之前,调用的线程将被阻止 - 您可能仍在处理此阶段的输出,具体取决于其他线程,或者它们可能在线程返回之前完成。