我有一个科学的应用程序,我通常与xargs
并行运行,但是这个方案会导致重复的JVM启动成本并忽略缓存的文件I / O和JIT编译器。我已经调整了代码以使用线程池,但我仍然坚持如何保存输出。
程序(即新程序的一个线程)读取两个文件,进行一些处理,然后将结果打印到标准输出。目前,我通过让每个线程将其结果字符串添加到BlockingQueue
来处理输出。只要布尔标志为真,另一个线程从队列中取出并写入文件。然后我awaitTermination
并将标志设置为false,触发文件关闭,程序退出。
我的解决方案似乎有点笨拙;实现这一目标的最简单,最好的方法是什么? 我应该如何将主要结果数据从多个线程写入单个文件?
如果答案是广泛适用的方法,则答案不需要特定于Java。
更新
我正在使用“STOP”作为毒丸。
while (true) {
String line = queue.take();
if (line.equals("STOP")) {
break;
} else {
output.write(line);
}
}
output.close();
我手动启动队列消耗线程,然后将作业添加到线程池,等待作业完成并最终中毒队列并加入使用者线程。
答案 0 :(得分:4)
这就是你想要的方式,让线程将他们的输出放到队列中然后让编写器耗尽它。
你唯一想做的事情就是让事情变得更清洁,而不是检查一个标志,只需将一个“全部完成”的标记放在队列上,作者可以用它来知道它已经完成了。这样就没有必要的带外信号。
这很简单,您可以使用众所周知的字符串,枚举或简单的共享对象。
答案 1 :(得分:2)
您可以使用ExecutorService。 提交将执行任务的Callable并在完成后返回字符串。
提交Callable后,您会收到Future,请存储这些参考资料,例如在列表中。
然后简单地遍历Future
并通过调用Future#get获取字符串。
这将阻止,直到任务完成(如果还没有),否则立即返回值。
示例:
ExecutorService exec = Executors.newFixedThreadPool(10);
List<Future<String>> tasks = new ArrayList<Future<String>>();
tasks.add(exec.submit(new Callable<String> {
public String call() {
//do stuff
return <yourString>;
}
}));
//and so on for the other tasks
for (Future<String> task : tasks) {
String result = task.get();
//write to output
}
答案 2 :(得分:1)
许多线程处理,一个线程写入和它们之间的消息队列是一个很好的策略。需要解决的问题是知道所有工作何时完成。一种方法是计算您启动的工作线程数,然后计算您获得的响应数量。像这样的伪代码:
int workers = 0
for each work item {
workers++
start the item's worker in a separate thread
}
while workers > 0 {
take worker's response from a queue
write response to file
workers--
}
如果工作人员在执行工作时可以找到更多工作项,这种方法也有效。只需在工作者响应中包含任何其他尚未处理的工作,然后像往常一样增加工作者数量并启动工作线程。
如果每个worker只返回一条消息,则可以使用Java的ExecutorService来执行返回结果的Callable实例。 ExecutorService的方法提供对Future实例的访问,当Callable完成其工作时,您可以从中获取结果。
因此,您首先要将所有任务提交给ExecutorService,然后遍历所有Futures并获得他们的回复。这样,您可以按照检查期货的顺序编写回复,这可能与他们完成工作的顺序不同。如果延迟不重要,那应该不是问题。否则,消息队列(如上所述)可能更合适。
答案 3 :(得分:0)
目前尚不清楚输出文件是否有某些已定义的顺序,或者您是否只是将数据转储到那里。我认为它没有订单。
我不明白为什么你需要额外的线程来写输出。只需synchronized
写入文件的方法,并在每个线程的末尾调用它。
答案 4 :(得分:0)
如果您有许多线程写入同一文件,最简单的方法是在任务中写入该文件。
final PrintWriter out =
ExecutorService es =
for(int i=0;i<tasks;i++)
es.submit(new Runnable() {
public void run() {
performCalculations();
// so only one thread can write to the file at a time.
synchornized(out) {
writeResults(out);
}
}
});
es.shutdown();
es.awaitTermination(1, TimeUnit.HOUR);
out.close();