Длинные вычисления приводят к тому, что ExecutorService перестает назначать новую работу - PullRequest
4 голосов
/ 04 июля 2011

Я оптимизирую файлы PNG, создав 5 процессов pngout.exe для работы с каталогом файлов PNG.Поскольку pngout является однопоточным, это приводит к значительному ускорению.Для оптимизации некоторых изображений требуется более 30 секунд, в то время как норма составляет <5 секунд.Проблема: </p>

  • Файл 1 большой, 2-5 - маленький, всего 50 файлов, но подробности об остальном не имеют значения.
  • Первые пять процессов pngout появляются нормально и начинают работать
  • 2-5 выход в течение 10 секунд
  • 1 занимает 45 секунд
  • Во время этого процесса не запускаются новые процессы pngout, несмотря на то, что четыре потока свободны
  • По завершении1, запускаются еще пять процессов.

Код:

private final ExecutorService pool = Executors.newFixedThreadPool(5);

    /* ^ instance var, below is in method */

    CompletionService<Boolean> comp = new ExecutorCompletionService<Boolean>(pool);
    List<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>();
    for (int i = 0; i < files.length; i++) {
        File infile = files[i];
        File outfile = new File(outdir, infile.getName());
        tasks.add(new CrushTask(crusher, infile, outfile));
    }
    for (Callable<Boolean> t : tasks)
        comp.submit(t);
    for (int i = 0; i < files.length; i++) {
        try {
            boolean res = comp.take().get();
            System.out.println(res);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Все файлы оптимизированы должным образом, эта часть кода работает.Проблема в том, что при ожидании на больших изображениях весь процесс значительно замедляется.Я получаю улучшение только на 40% по сравнению с однопоточным временем.

Что я делаю не так?

edit: Исправлена ​​проблема с использованием действительно уродливого кода.Проблема в том, что для получения выходного значения процессов, которые я порождаю (чтобы знать, когда они завершены и если они преуспели), я считывал их стандартный вывод, поскольку вызов waitFor зависал бы навсегда.Тем не менее, очевидно, что использование InputStreams приводит к тому, что потоки задыхаются.

Таким образом, чтобы получить выходное значение процессов, вместо использования этого:

private static int discardStdOut(Process proc) throws IOException {
    final InputStream is = proc.getInputStream();
    try {
        while (is.read() != -1)
            continue;
        return proc.exitValue();
    } finally {
        close(is);
    }
}

Я использую этот общий код:

private static int discardStdOut(Process proc) {
    int ret = -1;
    while (true) {
        try {
            ret = proc.exitValue();
            break;
        } catch (IllegalThreadStateException e) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e2) {
                e2.printStackTrace();
            }
        }
    }
    return ret;
}

Это брутто, но теперь система работает нормально и всегда работает 5 процессов.

позднее редактирование: StreamGobbler из здесь , вероятно, более уместно.

1 Ответ

0 голосов
/ 13 июля 2011

Вы получаете Нить голода.Вам нужно сделать сон или IO для Java, чтобы правильно управлять потоками.Это не неисправность операционной системы JVM неисправна.

...