java stdout.java,在Windows中:将无缓冲的stdout从C程序获取到Java程序InputStream中[现在用最少的示例!]...

解决了:

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\

etBeansProjects\\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%%

", currentProgress);

fflush(stdout);

在Java程序中,我使用Runtime.getRuntime().exec()运行C程序,并获取C程序的stdout和stderr的原始InputStream对象。 (stderr只是被另一个线程扔掉了。)我正在解析stdout流,查找那些消息(好吧,我当时在使用BufferedReader,但是现在,我正在尝试调试它,所以我m直接与低级InputStream字节源对话。)

问题是,尽管Java端没有缓冲(据我所知),并且C程序中有一个fflush,但stdout文本仅在C程序完成时立即全部显示。这与我在命令提示符下运行消息时所发出的消息有所不同,在命令提示符下运行它时,情况有所不同。

很明显,这里有一些缓冲。 Java中的Process工具是否正在执行一些我不知道的缓冲?如果是这样,是否有办法将其关闭? Windows中的fflush(stdout)是否不能以预期的方式工作? Windows的等效功能是什么?

谢谢。

注意:我发现了两个相关的stackoverflow,但是它们不能回答这个问题。特别是,我们可以修改C程序,没有行缓冲,并且我们正在做适当的刷新(据我们所知)。请参阅我想要在Windows上实时输出Runtime.getRuntime()。exec()和Unbuffered子进程stdout。

在我看来,这是一个疯狂的猜测,但您指出:

... I am writing a graphical front-end in Java, and I would like my program to capture those messages and update a GUI element.

...

In the Java program, I'm using Runtime.getRuntime().exec() to run the C program and grabbing the raw InputStream objects for C program's stdout and stderr. ... I'm parsing the stdout stream, looking for those messages ...

The problem is that although there is no buffering on the Java side (that I know of) and there's an fflush in the C program, the stdout text appears all at once only when the C program finishes.

我再次不得不猜测,因为您遗漏了重要细节和所有代码,但是如果前端是Swing GUI,那么您所描述的症状表明您正在尝试获取有关Swing事件线程的所有信息。 如果这是Swing应用程序,则解决方案是确保在后台线程(如SwingWorker提供的线程)中读取Stream的输出,并确保对正在读取的数据进行缓冲,然后 以线程安全的方式(再次使用SwingWorker,尤其是其发布/处理方法对)在GUI中显示它。

为了获得更好的帮助,请为我们提供更好的信息和代码,最好是一个小的示例,实际上是最少的示例程序,我们可以对其进行运行,测试和改进。

编辑

我的测试程序:

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 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();

}

}

}

您的担心是合理的。从EDT操纵UI是一个常见的陷阱。但是,在调试中,我实际上绕过了GUI。 InputStream在其自己的线程中,并且通过System.out.print()打印进度。我希望有人可能对此问题有所了解。但似乎病态必须听从您的建议,并提出一个最小的例子。完成后,我会更新我的帖子。

@TimothyMiller:您是否正确缓冲了输入?我想象您使用InputStreamReader,然后将其包装在BufferedReader中吗?例如,请看这个问题。

我最初使用的是缓冲读取器(如rgagnon.com/javadetails/java-0014.html中所示),但是存在相同的问题,因此我删除了额外的缓冲层。即使没有缓冲,问题也是一样。

@TimothyMiller:嗯,...让我测试您的代码,感谢您发布它!至少值得1+。

@TimothyMiller:嗯,...您的代码,...您在线程上调用run(),您知道那是正确的吗?如果您希望start()实际上充当后台线程,就应该始终调用它,对吗?

@TimothyMiller:那是我上面的答案。

@TimothyMiller:我发布了用于测试您的概念的代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值