我正在通过生成5个pngout.exe进程来优化PNG文件,以处理PNG文件的目录。由于pngout是单线程的,因此可以实现大幅加速。一些图像需要很长时间才能优化,超过30秒,而标准<5秒。问题:
代码:
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%的改进。
我做错了什么?
编辑:使用一些非常难看的代码修复了这个问题。问题是要获得我正在产生的进程的退出值(知道它们何时完成以及它们是否成功)我正在读取它们的stdout,因为调用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可能更合适。
答案 0 :(得分:0)
你正在获得线程饥饿。您需要为java进行睡眠或IO以正确管理线程。它不是JVMs故障操作系统的线程坏了。