我通过生成 5 个 pngout.exe 进程来优化 PNG 文件以处理 PNG 文件目录。由于 pngout 是单线程的,这会导致很大的加速。一些图像需要很长时间来优化,超过 30 秒,而标准是

  • 文件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% 的改进。

    我究竟做错了什么?

    编辑:修复了这个问题,使用了一些非常难看的代码。问题是为了获得我正在生成的进程的退出值(知道它们何时完成以及它们是否成功),我正在将它们的标准输出读取为空,因为调用 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 个进程在运行。

    后期编辑:来自 here 的 StreamGobbler 可能更合适。

    最佳答案

    您正在遭受线程饥饿。您需要为 java 执行 sleep 或 IO 以正确管理线程。这不是 JVM 故障操作系统线程被破坏。

    关于java - 长计算导致 ExecutorService 停止分配新工作,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6566630/

    10-14 17:02