【问题标题】:How to scale threads according to CPU cores?如何根据 CPU 内核扩展线程?
【发布时间】:2010-12-31 03:37:31
【问题描述】:

我想用 Java 中的多线程解决数学问题。我的数学问题可以分成多个工作单元,我想在几个线程中解决。

我不想让固定数量的线程在上面工作,而是让线程数量与 CPU 内核的数量相匹配。我的问题是,我在互联网上找不到一个简单的教程。我发现的都是固定线程的例子。

如何做到这一点?可以举个例子吗?

【问题讨论】:

    标签: java multithreading cpu-cores


    【解决方案1】:

    您可以使用静态运行时方法availableProcessors 来确定Java 虚拟机可用的进程数。确定可用处理器的数量后,创建该数量的线程并相应地拆分您的工作。

    更新:为了进一步说明,线程只是 Java 中的一个对象,因此您可以像创建任何其他对象一样创建它。因此,假设您调用上述方法并发现它返回 2 个处理器。惊人的。现在,您可以创建一个生成新线程的循环,并为该线程拆分工作,并触发该线程。这里有一些伪代码来说明我的意思:

    int processors = Runtime.getRuntime().availableProcessors();
    for(int i=0; i < processors; i++) {
      Thread yourThread = new AThreadYouCreated();
      // You may need to pass in parameters depending on what work you are doing and how you setup your thread.
      yourThread.start();
    }
    

    有关创建自己的线程的更多信息,head to this tutorial。此外,您可能希望查看 Thread Pooling 以创建线程。

    【讨论】:

    • 这基本上是正确的,但要注意使用英特尔“超线程”销售的处理器的性能。在四核上,这将返回 8 而不是 4,但您的性能实际上可能在 4 个线程后开始下降 - 所以我自己的基准测试告诉我:)
    • 嗨,好吧,不知道,这是可能的。但是当我将一项任务分成几个工作单元并且我需要最后一个工作步骤的所有部分解决方案时,这是如何完成的?当我有几个“yourThreads”时,我如何使用 join() ,因为我没有看到,这几个线程是如何区分的? :) 顺便说一句:你的线程池链接将我带到ibm.com/developerworks/library/j-jtp0730.html :)
    • 看这里的例子:java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/… 它会告诉你一个更精简的方法来创建和管理线程池......一开始可能看起来更复杂,但与大多数事情一样,它是更复杂,因为如果它更简单,您只会更快地遇到限制。
    【解决方案2】:

    您可能也想查看 java.util.concurrent 框架来处理这些东西。 比如:

    ExecutorService e = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    // Do work using something like either
    e.execute(new Runnable() {
            public void run() {
                // do one task
            }
        });
    

        Future<String> future = pool.submit(new Callable<String>() {
            public String call() throws Exception {
                return null;
            }
        });
        future.get();  // Will block till result available
    

    这比处理自己的线程池等要好得多。

    【讨论】:

    • 嗨 DaveC,嗯,以前不知道,所以我会看看这个。它可以根据可用的 cpu 核心进行扩展吗?因为我在您的简短示例中看不到这一点。最好的问候,安德烈亚斯
    • java.util.concurrent 具有高度可扩展性
    • 具有可用处理器数量的固定大小的池通常是 CPU 绑定进程的最佳选择。这里的第一个例子就是你需要做的。
    • 如已接受答案的第一条评论中所述,最好使用报告的“处理器”数量的一半,原因有两个:1.如果你有超线程,实数处理器的数量是报告的一半,并且 2. 它允许系统其余部分(操作系统和其他程序)运行一些处理能力。
    【解决方案3】:

    选项 1:

    newWorkStealingPool 来自Executors

    public static ExecutorService newWorkStealingPool()
    

    使用所有可用处理器作为其目标并行级别创建一个工作窃取线程池。

    使用此 API,您无需将核心数传递给 ExecutorService

    grepcode实现这个API

    /**
         * Creates a work-stealing thread pool using all
         * {@link Runtime#availableProcessors available processors}
         * as its target parallelism level.
         * @return the newly created thread pool
         * @see #newWorkStealingPool(int)
         * @since 1.8
         */
        public static ExecutorService newWorkStealingPool() {
            return new ForkJoinPool
                (Runtime.getRuntime().availableProcessors(),
                 ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                 null, true);
        }
    

    选项 2:

    newFixedThreadPool API 来自 Executorsother newXXX constructors,返回 ExecutorService

    public static ExecutorService newFixedThreadPool(int nThreads)
    

    将 nThreads 替换为 Runtime.getRuntime().availableProcessors()

    选项 3:

    ThreadPoolExecutor

    public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)
    

    Runtime.getRuntime().availableProcessors() 作为参数传递给maximumPoolSize

    【讨论】:

      【解决方案4】:

      Doug Lea(并发包的作者)有这篇论文可能相关: http://gee.cs.oswego.edu/dl/papers/fj.pdf

      Java SE 7 中添加了 Fork Join 框架。以下是更多参考资料:

      http://www.ibm.com/developerworks/java/library/j-jtp11137/index.html Brian Goetz 的文章

      http://www.oracle.com/technetwork/articles/java/fork-join-422606.html

      【讨论】:

        【解决方案5】:

        标准方法是 Runtime.getRuntime().availableProcessors() 方法。 在大多数标准 CPU 上,您将在此处返回最佳线程数(这不是实际的 CPU 核心数)。因此,这就是您要寻找的。​​p>

        例子:

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

        不要忘记像这样关闭执行器服务(否则您的程序不会退出):

        service.shutdown();
        

        这里只是简要概述如何设置基于未来的 MT 代码(题外话,用于说明):

        CompletionService<YourCallableImplementor> completionService = 
            new ExecutorCompletionService<YourCallableImplementor>(service);
            ArrayList<Future<YourCallableImplementor>> futures = new ArrayList<Future<YourCallableImplementor>>();
            for (String computeMe : elementsToCompute) {
                futures.add(completionService.submit(new YourCallableImplementor(computeMe)));
            }
        

        然后你需要跟踪你期望有多少结果并像这样检索它们:

        try {
          int received = 0;
          while (received < elementsToCompute.size()) {
             Future<YourCallableImplementor> resultFuture = completionService.take(); 
             YourCallableImplementor result = resultFuture.get();
             received++; 
          }
        } finally {
          service.shutdown();
        }
        

        【讨论】:

        • shutdown call 应该放在try finally
        • @ChristopheRoussy 你说的很对,我已经相应地修改了sn-p,谢谢!
        【解决方案6】:

        在 Runtime 类上,有一个名为 availableProcessors() 的方法。你可以用它来计算你有多少个 CPU。由于您的程序受 CPU 限制,您可能希望(至多)每个可用 CPU 有一个线程。

        【讨论】:

        • 嗨 Jason 和 Eric(我对你的两个答案都使用了一条评论,因为它基本相同)。好的,这很好检查,但这将是第一部分。当我有核心数量时,我必须让线程与这个核心数量一样可变。我在openbook.galileodesign.de/javainsel5/…(德语!)之前尝试过这个例子,它使用了一个固定的线程。但我想在双核环境中使用 2 核,在四核环境中使用 4 核进行相同的编程。我不想手动更改它。这可能吗?谢谢! :)
        • @Andreas - 查看我对帖子所做的更新。我认为这将有助于澄清问题。
        猜你喜欢
        • 2021-10-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多