Process.waitFor()、线程和 InputStreams

Posted

技术标签:

【中文标题】Process.waitFor()、线程和 InputStreams【英文标题】:Process.waitFor(), threads, and InputStreams 【发布时间】:2011-01-10 04:18:15 【问题描述】:

在伪代码中,这就是我正在做的事情:

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

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

根据我所看到的,我猜测 command 不需要将其所有输出转储到由 getInputStream()getErrorStream() 提供的流中,从而允许流为空。

我的试验结果如下:

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

文档仅说明:

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

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

文档仅说明:

获取子进程的错误流。流从这个 Process 对象表示的进程的错误输出流中获取数据。

实现说明:缓冲输入流是个好主意。

一个user reports 说他必须自己关闭流,但至少在部分时间我得到一个异常,表明当我尝试这样做时流已经关闭。

编辑:getOutputStream 更改为getInputStream,现在出现在上面。

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

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

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

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

【问题讨论】:

【参考方案1】:

如果您的外部进程在其stdin 上需要某些东西,您必须关闭getOutputStream。否则你将永远waitFor

这里是来自 JavaWorld 的 When Runtime.exec() won't article,它描述了 exec 方法的不同陷阱以及如何避免它们。

根据我的经验,最好使用子进程的 STDOUT 和 STDERR(直到它们 EOF),然后阻止 waitFor。希望此时您不必等待太久。

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

我真的希望waitFor 有一些有意义的超时,并且当您决定放弃其监控时,Process 有记录的方式来清理其资源。

【讨论】:

如果使用其他流,是否也必须关闭它们?当进程终止时它们是否隐式关闭? @Kaleb 我已在我的帖子中添加了对您问题的答案。 关于JavaWorld的文章,最终解决方案有一个bug。如果一个进程很快终止,errorGobbler 和 outputGobbler 可能还没有运行和消耗数据。代码应该是:proc.waitFor(); errorGobbler.join();输出Gobbler.join();。这迫使主线程等待,直到流完成读取输入。 要求一些人在这里发布代码示例会不会太过分了? @DivyangShah。由于无法指定超时,理论上它可能会永远等待。这确实是Process 的主要问题之一。【参考方案2】:

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

getOutputStream 获取输出 子进程的流。输出到 流被输送到标准 进程的输入流 由这个 Process 对象表示。 实施说明:这是个好主意 用于缓冲输出流。 返回:连接的输出流 到子进程的正常输入。

我将其作为主进程的输出流读取并附加到子进程的标准输入,因此当您写入 getOutputStream().write() 时,您实际上是在标准输入上写入。

你可能想使用 .getInputStream() 吗?

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

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

使当前线程等待,如果 必要的,直到过程 由这个 Process 对象表示有 终止。该方法返回 如果子流程有 已经终止。如果子进程 尚未终止,调用 线程将被阻塞,直到 子进程退出。

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

【讨论】:

我的问题中的getOutputStream() 是一个错字。在我的实际代码中确实是getInputStream(),并且不会编译,因为处理函数需要InputStream 我认为您对我的线程仍在处理输出是完全正确的。大多数时候,当我在调试模式下运行时,一切都运行成功,因为一切都运行得慢得多。在没有调试器的情况下运行时,它通常会失败。我稍后会发布我的解决方法。

以上是关于Process.waitFor()、线程和 InputStreams的主要内容,如果未能解决你的问题,请参考以下文章

process.waitFor() 永远不会返回

怎样判断子进程已经结束 process.waitFor;的问题

解决JAVA Process死锁

Process.waitFor()方法的返回值

Process.waitFor()方法的返回值

Process.waitFor()方法的返回值