8

我正在寻找一个负载平衡的线程池,到目前为止没有成功。(不确定负载平衡是否是正确的措辞)。让我解释一下我试图实现的目标。

第 1 部分:我有工作,有 8 到 10 个单项任务。在 6 核 CPU 上,我让 8 个线程并行处理这些任务,这似乎可以提供最佳性能。当一项任务准备就绪时,另一项任务就可以开始了。一旦完成所有十项任务,就完成了完整的工作。通常一项工作在 30 到 60 秒内完成。

第二部分:不幸的是,有时这项工作需要两个多小时。由于必须计算的数据量,这是正确的。坏事是,在 job1 运行时没有其他作业可以启动(假设所有线程具有相同的持续时间),因为它正在使用所有线程。

我的第一个想法:拥有 12 个线程,最多允许三个并行作业。但是:这意味着,当只有 1 个工作时,cou 并没有完全实现。

我正在寻找一种解决方案,以便在没有其他工作时为第一个工作提供完整的 CPU 能力。但是,当另一个作业需要在另一个作业运行时启动时,我希望将 CPU 功率分配给这两个作业。当出现第三个或第四个工作时,我希望将 CPU 功率公平地分配给所有四个工作。

我很欣赏你的回答...

提前致谢

4

3 回答 3

6

一种可能是使用ThreadPoolExecutor具有不同类型任务队列的标准

public class TaskRunner {
  private static class PriorityRunnable implements Runnable,
            Comparable<PriorityRunnable> {
    private Runnable theRunnable;
    private int priority = 0;
    public PriorityRunnable(Runnable r, int priority) {
      this.theRunnable = r;
      this.priority = priority;
    }

    public int getPriority() {
      return priority;
    }

    public void run() {
      theRunnable.run();
    }

    public int compareTo(PriorityRunnable that) {
      return this.priority - that.priority;
    }
  }

  private BlockingQueue<Runnable> taskQueue = new PriorityBlockingQueue<Runnable>();

  private ThreadPoolExecutor exec = new ThreadPoolExecutor(8, 8, 0L,
            TimeUnit.MILLISECONDS, taskQueue);

  public void runTasks(Runnable... tasks) {
    int priority = 0;
    Runnable nextTask = taskQueue.peek();
    if(nextTask instanceof PriorityRunnable) {
      priority = ((PriorityRunnable)nextTask).getPriority() + 1;
    }
    for(Runnable t : tasks) {
      exec.execute(new PriorityRunnable(t, priority));
      priority += 100;
    }
  }
}

这里的想法是,当你有一份新工作时,你打电话给

taskRunner.runTasks(jobTask1, jobTask2, jobTask3);

并且它将以这样一种方式将任务排队,使它们与队列中的任何现有任务(如果有的话)很好地交错。假设您有一个作业排队,其任务的优先级编号为 j 1 t 1 =3、j 1 t 2 =103 和 j 1 t 3 =203。在没有其他作业的情况下,这些任务会以最快的速度一个接一个地执行。但是,如果您提交另一个包含三个任务的作业,这些任务将被分配优先级编号 j 2 t 1 =4、j 2 t 2 =104 和 j 2 t 3 =204,这意味着队列现在看起来像

j 1 t 1 , j 2 t 1 , j 1 t 2 , j 2 t 2等。

然而,这并不完美,因为如果所有线程当前都在工作(来自工作 1 的任务),那么工作 2 的第一个任务在其中一个工作 1 任务完成之前无法启动(除非有一些外部方法可供您检测这并中断并重新排队作业 1 的一些任务)。使事情更公平的最简单方法是将运行时间较长的任务分解为更小的部分并将它们作为单独的任务排队 - 您需要达到一个点,即每个单独的作业涉及的任务多于池中的线程数,这样某些任务将始终在队列中开始,而不是直接分配给线程(如果有空闲线程,则将exec.execute()任务直接传递给线程,而根本不经过队列)。

于 2013-01-19T15:06:02.647 回答
1

正如 Kanaga 建议的那样,最简单的做法是超额订阅 CPU,但每个线程启动 8 个线程。竞争可能会产生一些开销,但如果你得到一个单一的工作情况,它将充分利用 CPU。操作系统将处理为每个线程分配时间。

您的“第一个想法”也可以。如果空闲线程实际上并未执行任务,则它们不会从 8 个工作线程中获取资源。但是,当有多个作业正在运行时,这不会均匀地分配 cpu 资源。

您是否有一个设置可以测试这些不同的管道以查看它们对您的执行情况?

于 2013-01-19T15:20:05.033 回答
1

我想因为你的机器是6核CPU。每个工作线程最好有 6 个工作线程。因此,当一个线程获得新工作时,它会启动多达六个并行工作人员来处理单个工作。这将确保在一次只有一项作业时消耗全部 cpu 功率。

另请查看 Java 7 中的 Fork 和 Join 概念。
References_1
References_2
References_3
References_4

还可以了解 newcachedthreadpool()

Java newCachedThreadPool() 与 newFixedThreadPool

于 2013-01-19T14:43:48.700 回答