В Windows: получите небуферизованный стандартный вывод из программы на C в Java-программу InputStream [Теперь с минимальным примером!]

1

РЕШИТЬ:

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.

Теги:
stdout
ipc

1 ответ

1
Лучший ответ

Дикая догадка с моей стороны, но вы заявляете:

... Я пишу графический интерфейс в 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();
      }
   }
}
  • 0
    Ваше беспокойство разумно. Управление пользовательским интерфейсом от EDT является распространенной ошибкой. Тем не менее, в моей отладке я на самом деле обходил графический интерфейс. InputStream находится в своем собственном потоке, а прогресс печатается через System.out.print (). Я надеялся, что кто-то может иметь некоторые знания об этой проблеме. Но, похоже, мне придется последовать вашему совету и привести минимальный пример. Я обновлю свой пост, когда закончу.
  • 0
    @TimothyMiller: Вы правильно буферизуете свои данные? Я полагаю, что вы используете InputStreamReader, вы затем оборачиваете это в BufferedReader? Пожалуйста, посмотрите на этот вопрос, например.
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню