简单的异步I / O:许多线程,一个文件

时间:2011-07-09 20:46:03

标签: java multithreading file-io asynchronous parallel-processing

我有一个科学的应用程序,我通常与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();

我手动启动队列消耗线程,然后将作业添加到线程池,等待作业完成并最终中毒队列并加入使用者线程。

5 个答案:

答案 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();