РЕШИТЬ:
HovecraftFullOfEels нашел мою ошибку. См. Комментарии к его ответу ниже. Я вызывал run() в объекте Thread, когда мне приходилось запускать start(). В моей первоначальной программе это означало, что он заблокирован на 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, и я хотел бы, чтобы моя программа захватила эти сообщения и обновила элемент графического интерфейса.
Соответствующий программный код C выглядит следующим образом:
printf ("progress_msg: %d%%\n", currentProgress);
fflush(stdout);
В программе Java я использую Runtime.getRuntime().exec()
для запуска программы C и захвата необработанных объектов InputStream для программных stdout и stderr. (Stderr просто отбрасывается другим потоком.) Я разбираю поток stdout, ища эти сообщения (ну, я был, используя BufferedReader, но прямо сейчас, я пытаюсь отладить это, m, разговаривая напрямую с источником байта нижнего уровня InputStream.)
Проблема в том, что, хотя на стороне Java нет (но я не знаю) буферизации на стороне Java и там есть fflush
в программе C, текст stdout появляется сразу, только когда заканчивается программа C. Это не похоже на то, когда я запускаю его из командной строки, где сообщения появляются постепенно, как ожидалось.
Так ясно, что происходит буферизация. Является ли средство Process в Java делать некоторую буферизацию, о которой я не знаю? Если да, есть ли способ отключить его? Неужели fflush(stdout)
в Windows не работает ожидаемым образом? Каков правильный эквивалент Windows?
Благодарю.
Примечание. Я нашел два связанных stackoverflows, но они не отвечают на этот вопрос. В частности, мы МОЖЕМ модифицировать программу C, нет буферизации строк, и мы делаем правильный флеш (насколько нам известно). См. " Я хочу, чтобы в реальном времени выводился мой Runtime.getRuntime(). Exec() и Unbuffered subprocess stdout в Windows.
Дикая догадка с моей стороны, но вы заявляете:
... Я пишу графический интерфейс в Java, и я бы хотел, чтобы моя программа захватывала эти сообщения и обновляла элемент графического интерфейса.
...В программе Java я использую Runtime.getRuntime(). Exec() для запуска программы C и захвата необработанных объектов InputStream для программных stdout и stderr.... Я разбираю поток stdout, ища эти сообщения...
Проблема в том, что, хотя на стороне Java нет (но я не знаю) буферизации на стороне Java и там есть fflush в программе C, текст stdout появляется сразу, только когда заканчивается программа C.
Я снова должен угадать, поскольку вы оставили важные детали, весь код, но если ваш интерфейс - это графический интерфейс Swing, то описанные вами симптомы предполагают, что вы пытаетесь получить всю информацию о потоке событий Swing. Если это приложение Swing, решение состоит в том, чтобы удостовериться, что вы прочитали вывод Stream в фоновом потоке, таком как предоставленный SwingWorker, чтобы убедиться, что вы буферизируете эти данные, которые вы читаете, а затем отображать его в GUI поточно-безопасным способом (опять же с использованием SwingWorker, особенно его пары публикации/процесса).
Для лучшей помощи, пожалуйста, дайте нам лучшую информацию и код, предпочтительно небольшую, по сути, минимальную, примерную программу, которую мы можем запускать, тестировать и совершенствовать.
редактировать
Моя тестовая программа:
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();
}
}
}