并行化 for 循环

2022-09-01 18:20:27

我有一个for循环,其中迭代时的计算不依赖于先前迭代中完成的计算。i

我想并行化循环(我的代码是java),以便多个迭代的计算可以在多个处理器上同时运行。我是否应该为每次迭代的计算创建一个线程,即要创建的线程数等于迭代次数(for 循环中的迭代次数很大)?如何做到这一点?for


答案 1

下面是一个小示例,你可能会发现它有助于开始并行化。它假设:

  1. 创建一个对象,其中包含每次计算迭代的输入。Input
  2. 创建一个对象,其中包含计算每次迭代的输入的输出。Output
  3. 您希望传入输入列表并一次取回所有输出列表。
  4. 您的输入是一个合理的工作量,因此开销不会太高。

如果您的计算非常简单,那么您可能需要考虑批量处理它们。您可以通过在每个输入中放入100来做到这一点。它使用与系统中处理器一样多的线程。如果您正在处理纯粹的CPU密集型任务,那么这可能是您想要的数字。如果他们被阻止等待其他内容(磁盘,网络,数据库等),您可能希望走得更高。

public List<Output> processInputs(List<Input> inputs)
        throws InterruptedException, ExecutionException {

    int threads = Runtime.getRuntime().availableProcessors();
    ExecutorService service = Executors.newFixedThreadPool(threads);

    List<Future<Output>> futures = new ArrayList<Future<Output>>();
    for (final Input input : inputs) {
        Callable<Output> callable = new Callable<Output>() {
            public Output call() throws Exception {
                Output output = new Output();
                // process your input here and compute the output
                return output;
            }
        };
        futures.add(service.submit(callable));
    }

    service.shutdown();

    List<Output> outputs = new ArrayList<Output>();
    for (Future<Output> future : futures) {
        outputs.add(future.get());
    }
    return outputs;
}

答案 2

不应手动执行线程处理。相反:

  • 创建一个大小合理的线程池执行器服务(如果您的计算不执行 IO,请使用具有内核的任意数量的线程)。
  • 运行一个循环,该循环将每个单独的计算提交到执行器服务并保留生成的对象。请注意,如果每个计算仅包含少量工作,这将产生大量开销,甚至可能比单线程程序慢。在这种情况下,请提交按照 mdma 建议执行计算数据包的作业。Future
  • 运行第二个循环,从所有 s 收集结果(它将隐式等待所有计算完成)Future
  • 关闭执行程序服务