我正在通过生成 5 个 pngout.exe 进程来优化 PNG 文件,以处理 PNG 文件的目录。由于 pngout 是单线程的,这会导致很大的加速。有些图像需要很长时间来优化,超过 30 秒,而标准是 <5 秒。问题:
- 文件1大,2-5小,共50个文件,其余细节无关。
- 前五个 pngout 进程正常生成并开始工作
- 10秒内2-5退出
- 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 个进程在运行。
后期编辑:来自这里的StreamGobbler可能更合适。