假设我有以下代码,我不想通过将工作负载分散到我的PC的多个CPU核上来优化它:
double[] largeArray = getMyLargeArray();
double result = 0;
for (double d : largeArray)
result += d;
System.out.println(result);
在本例中,我可以在多个线程上分发for-循环中完成的工作,并在继续打印result
之前验证线程是否都已结束。因此,我想出了一个类似这样的东西:
final double[] largeArray = getMyLargeArray();
int nThreads = 5;
final double[] intermediateResults = new double[nThreads];
Thread[] threads = new Thread[nThreads];
final int nItemsPerThread = largeArray.length/nThreads;
for (int t = 0; t<nThreads; t++) {
final int t2 = t;
threads[t] = new Thread(){
@Override public void run() {
for (int d = t2*nItemsPerThread; d<(t2+1)*nItemsPerThread; d++)
intermediateResults[t2] += largeArray[d];
}
};
}
for (Thread t : threads)
t.start();
for (Thread t : threads)
try {
t.join();
} catch (InterruptedException e) { }
double result = 0;
for (double d : intermediateResults)
result += d;
System.out.println(result);
假设largeArray
的长度可以被nThreads
除以。此解决方案工作正常。
但是,我遇到的问题是,在我的程序中经常会出现上述for-循环线程,这会导致大量开销,这是由于线程的创建和垃圾收集造成的。因此,我正在考虑使用ThreadPoolExecutor
修改我的代码。然后,给出中间结果的线程将在下一次执行中被重用(在本例中是求和)。
由于我将中间结果存储在一个大小必须事先知道的数组中,所以我考虑使用一个大小固定的线程池。但是,我很难让线程知道它应该在数组中哪个位置存储它的结果。我应该定义我自己的ThreadFactory
吗?
还是更好地使用由方法ExecutorService
创建的Executors.newSingleThreadExecutor(ThreadFactory myNumberedThreadFactory)
数组?
请注意,在我的实际程序中,很难用另一种类型的东西来替换double[] intermediateResults
。我更喜欢一种仅限于创建正确的线程池的解决方案。
发布于 2014-03-12 11:34:16
但是,我很难让
thread
知道它应该在array
中哪个位置存储它的结果。我应该定义我自己的ThreadFactory
吗?
没必要那么做。执行器使用的接口(Runnable
和Callable
)由线程运行,您可以向您想传递的实现传递任何参数(例如,数组、开始索引和结束索引)。
ThreadPoolExecutor
确实是一个很好的解决方案。如果您有可运行的结果,也可以查看FutureTask
。
发布于 2014-03-12 11:43:02
ExecutorService
为您提供了通过Future
接口从线程池获取结果的API:
Future<Double> futureResult = executorService.submit(new Callable<Double>() {
Double call() {
double totalForChunk = 0.0;
// do calculation here
return totalForChunk;
}
});
现在,您需要做的就是提交任务(Callable
实例)并等待结果可用:
List<Future<Double>> results = new ArrayList<Double>();
for (int i = 0; i < nChunks; i++) {
results.add(executorService.submit(callableTask));
}
或者更简单:
List<Future<Double>> results = executorService.invokeAll(callableTaskList);
其余的都很简单,可以在results
上迭代并收集总计:
double total = 0.0;
for (Future<Double> result : results) {
total += result.get(); // this will block until your task is completed by executor service
}
尽管如此,您并不关心您在executor服务中有多少线程。您只需提交任务并在可用时收集结果。
发布于 2014-03-12 11:35:05
最好是创建"worker“线程,这些线程接收要从队列中执行的工作的信息。然后,您的流程将是创建一个最初为空的WorkQueue,然后创建并启动工作线程。每个工作线程将从队列中拾取其工作,完成工作,并将工作放在一个“已完成”队列上,供主线程拾取和处理。
https://stackoverflow.com/questions/22361522
复制相似问题