解决:
HovecraftFullOfEels发现了我的错误。请参阅下面的答案评论。我应该调用start()时在Thread对象上调用run()。在我的原始程序中,这意味着它在stderr上被阻止,直到程序退出然后立即获得stdout。
更新
根据要求,我制作了一个最小的例子。这是制作人:
package producer;
public class Producer {
public static void main(String[] args) throws InterruptedException {
for (int i=0; i<10; i++) {
System.out.println(i);
System.out.flush();
Thread.sleep(1000);
}
}
}
这是消费者:
package consumer;
import java.io.IOException;
import java.io.InputStream;
public class Consumer {
static class Dumper extends Thread {
InputStream r;
boolean print;
Dumper(InputStream r, boolean print) {
this.r = r;
this.print = print;
}
@Override
public void run() {
int c;
try {
while ((c = r.read()) != -1) {
if (print) System.out.print((char)c);
}
r.close();
} catch (IOException ex) {}
}
}
public static void main(String[] args) throws Exception {
Process p = Runtime.getRuntime().exec("java -jar C:\\Users\\millerti\\Documents\\NetBeansProjects\\Producer\\dist\\Producer.jar");
new Dumper(p.getErrorStream(), false).run();
new Dumper(p.getInputStream(), true).run();
p.waitFor();
}
}
预期行为:由于生产者刷新其输出并且消费者完全没有缓冲,消费者应该实时获得生产者的输出。
观察到的行为:只有当制作人完成时,消费者才会立即收到制作人的输出。
原帖:
我正在与Windows应用程序上的其他开发人员合作。她编写了一个C程序(我们可以修改),它执行媒体转换并将进度消息(百分比)打印到stdout。我正在用Java编写图形前端,我希望我的程序能够捕获这些消息并更新GUI元素。
相关的C程序代码如下所示:
printf ("progress_msg: %d%%\n", currentProgress);
fflush(stdout);
在Java程序中,我使用Runtime.getRuntime().exec()
来运行C程序并获取C程序的stdout和stderr的原始InputStream对象。 (stderr只是被另一个线程抛弃了。)我正在解析stdout流,寻找那些消息(好吧,我是,使用BufferedReader,但是现在,我正试图调试这个,所以我直接与低级别的InputStream字节源进行对话。)
问题在于,尽管Java端(我知道)没有缓冲,并且C程序中有fflush
,但只有当C时才会出现stdout文本节目结束。这与我从命令提示符运行它不同,其中消息按预期逐渐出现。
很明显,有一些缓冲正在进行。 Java中的Process工具是否在做一些我不了解的缓冲?如果是这样,有没有办法将其关闭? Windows中的fflush(stdout)
无法以预期的方式运行吗?什么是正确的Windows等价物?
感谢。
注意:我找到了两个相关的stackoverflow,但他们没有回答这个问题。特别是,我们可以修改C程序,没有行缓冲,我们正在进行适当的刷新(据我们所知)。请参阅I want realtime output of my Runtime.getRuntime().exec()和Unbuffered subprocess stdout on windows。
答案 0 :(得分:1)
我的一个疯狂的猜测,但你说:
... 我正在用Java编写图形前端,我希望我的程序能够捕获这些消息并更新GUI元素。
......在Java程序中,我使用Runtime.getRuntime()。exec()来运行C程序并获取C程序的stdout和stderr的原始InputStream对象。 ...我正在解析stdout流,正在寻找那些消息...
问题是虽然Java端没有缓冲(我知道)并且在C程序中有一个fflush,但只有当C程序完成时才会出现stdout文本。
我再次猜测,因为你遗漏了重要的细节,所有代码,但如果你的前端是一个Swing GUI,那么你所描述的症状表明你试图获得有关Swing的所有信息事件线程。如果这是一个Swing应用程序,解决方案是确保在后台线程(例如SwingWorker提供的线程)中读取Stream的输出,以确保缓冲您正在读取的数据,然后以线程安全的方式在GUI中显示它(再次使用SwingWorker,尤其是其发布/处理方法对)。
为了获得更好的帮助,请向我们提供更好的信息和代码,最好是我们可以运行,测试和改进的一小部分,实际上是最小的example program。
修改强>
我的测试程序:
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Consumer {
static class StreamGobbler implements Runnable {
private String name;
private boolean print;
private Scanner scanner;
public StreamGobbler(String name, InputStream inputStream, boolean print) {
this.name = name;
this.print = print;
scanner = new Scanner(inputStream);
}
@Override
public void run() {
System.out.printf("in %s run method%n", name);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (print) {
String output = String.format("From %s: %s%n", name, line);
System.out.printf(output);
}
}
if (scanner != null) {
scanner.close();
}
}
}
private static final String PRODUCER_PATH = "C:/Users/Pete/Documents/Fubar/Java/producer.jar";
public static void main(String[] args) {
List<String> command = new ArrayList<>();
command.add("java.exe");
command.add("-jar");
command.add(PRODUCER_PATH);
try {
ProcessBuilder pBuilder = new ProcessBuilder(command);
Process process = pBuilder.start();
StreamGobbler errorGobbler = new StreamGobbler("Error Gobbler", process.getErrorStream(), true);
StreamGobbler inputGobbler = new StreamGobbler("Input Gobbler", process.getInputStream(), true);
new Thread(errorGobbler).start();
new Thread(inputGobbler).start();
process.waitFor();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}