以负载均衡方式在 4 个线程上分配 10 个无限作业 (Java)
Distribute 10 infinite jobs over 4 threads in a load-balanced manner (Java)
我有 10 个计算作业 需要(接近)无限时间。例如:计算PI的下一位,solve一个NP-hard约束满足问题等
我有 4 个线程(所以在 8 个内核 的机器上有一个 4 个线程的线程池,所以我还剩下一些内核避免实时锁定机器和进程)。
使用 Java8,如何在这 4 个线程中分配这 10 个作业?
这是个坏主意:
ExecutorService es = Executors.newFixedThreadPool(4);
for (Job j : jobs) {
es.submit(j);
}
因为 4 个作业将开始,但 none 将完成,因此作业 5-10 永远不会开始。
如果我照看 10 分钟,我希望每个作业有 运行 大约 4 分钟。 20 分钟后,每个作业有 运行 大约 8 分钟,等等。处理这个问题的典型模式是什么? (如果需要,我可以实现一种在预设时间后暂停计算的方法。)
如果您需要并行执行 10 个作业 - 只需 运行 10 个线程。
将 Executors.newFixedThreadPool(4)
更改为 Executors.newFixedThreadPool(10)
。
我认为您正在寻找 WorkStealingPool:
static ExecutorService executor = Executors.newWorkStealingPool(4);
private static Map<Integer, AtomicInteger> map = new ConcurrentHashMap<>();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
executor.submit(new Worker(i)) ;
}
Thread.sleep(10000);
System.out.println(map);
}
private static class Worker implements Runnable {
private final int k;
public Worker(int k) {
this.k = k;
}
@Override
public void run() {
map.putIfAbsent(k, new AtomicInteger(0));
map.get(k).getAndIncrement();
executor.submit(new Worker(this.k));
// Also possible to resubmit current job
//executor.submit(this);
}
}
我对 "Job that never finishes." 的想法有些困扰,我会称它为其他名称,例如 "long-running computation" 或...
如果你有十个,而你只能负担四个线程来处理它们,那么你唯一的选择就是将它们分解成有限的 "sub-jobs" do 完成,然后编写一个调度程序,不断向四个可用线程提供子作业。
但这将复制线程系统应该为您完成的大部分工作。
我只做十个线程。如果您 运行ning 在只有四个可用核心的机器上 运行 十个线程,OS 会自动将您的长 运行ning 作业分解为 "sub-jobs"(即时间片),并在四核上公平调度。
在四个线程之间分配十个作业的任务和仅利用四个CPU的任务(我这里使用CPU作为核心的同义词 为了简单起见)你的十个工作有点不同。
四个线程
将线程数限制为四个并不能保证它们会坚持使用四个 CPU 并且不会使用其他线程。 OS 可以根据需要在所有可用的 CPU 之间随机播放您的线程。您唯一可以保证的是,您的程序将无法利用所有 CPU 资源的 50% 以上(假设您有八个 CPU)。
但您不太可能设法利用这 50%。尽管您的作业主要是面向 CPU 的,但它们仍然可能需要不时地读取和写入内存。当一个线程在这样的 readings/writings 上错过了缓存并等待数据被传送到处理器时,这个处理器会暂停该线程并可以在另一个线程中做一些工作。在您的情况下,它将无事可做,只是闲置直到数据到达。因此,您的 CPU 很可能未得到充分利用。
如果你决定采用这种方法,你需要将你的工作分解成小任务并将它们提供给执行者,正如@James Large 所说。您可以将 WorkStealingPool
与四个线程一起使用(正如@Alexey Soshin 所建议的那样),或者创建一个具有十个线程的池并使用 Semaphore
并使用四个许可并将公平设置为 true
。在后一种情况下,您的线程必须使用循环,在每次迭代开始时获取许可并在结束时释放它们。每次迭代将代表一小部分工作。
四个CPUs
有一些机制可以指定特定的 CPU 来处理您的任务。
在 Linux 中的进程级别,您可以使用 special commands 将您的进程绑定到特定的 CPU。这将允许您创建十个线程并让 OS 在四个 CPU 上完成所有平衡。
在线程级别,您可以尝试 Java Affinity library 来自 OpenHFT。它允许在您的 Java 代码中将线程绑定到 CPUs。问题是十个线程不能在没有提醒的情况下被分配到四个CPU之间,所以很难平衡它们。
我有 10 个计算作业 需要(接近)无限时间。例如:计算PI的下一位,solve一个NP-hard约束满足问题等
我有 4 个线程(所以在 8 个内核 的机器上有一个 4 个线程的线程池,所以我还剩下一些内核避免实时锁定机器和进程)。
使用 Java8,如何在这 4 个线程中分配这 10 个作业?
这是个坏主意:
ExecutorService es = Executors.newFixedThreadPool(4);
for (Job j : jobs) {
es.submit(j);
}
因为 4 个作业将开始,但 none 将完成,因此作业 5-10 永远不会开始。
如果我照看 10 分钟,我希望每个作业有 运行 大约 4 分钟。 20 分钟后,每个作业有 运行 大约 8 分钟,等等。处理这个问题的典型模式是什么? (如果需要,我可以实现一种在预设时间后暂停计算的方法。)
如果您需要并行执行 10 个作业 - 只需 运行 10 个线程。
将 Executors.newFixedThreadPool(4)
更改为 Executors.newFixedThreadPool(10)
。
我认为您正在寻找 WorkStealingPool:
static ExecutorService executor = Executors.newWorkStealingPool(4);
private static Map<Integer, AtomicInteger> map = new ConcurrentHashMap<>();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
executor.submit(new Worker(i)) ;
}
Thread.sleep(10000);
System.out.println(map);
}
private static class Worker implements Runnable {
private final int k;
public Worker(int k) {
this.k = k;
}
@Override
public void run() {
map.putIfAbsent(k, new AtomicInteger(0));
map.get(k).getAndIncrement();
executor.submit(new Worker(this.k));
// Also possible to resubmit current job
//executor.submit(this);
}
}
我对 "Job that never finishes." 的想法有些困扰,我会称它为其他名称,例如 "long-running computation" 或...
如果你有十个,而你只能负担四个线程来处理它们,那么你唯一的选择就是将它们分解成有限的 "sub-jobs" do 完成,然后编写一个调度程序,不断向四个可用线程提供子作业。
但这将复制线程系统应该为您完成的大部分工作。
我只做十个线程。如果您 运行ning 在只有四个可用核心的机器上 运行 十个线程,OS 会自动将您的长 运行ning 作业分解为 "sub-jobs"(即时间片),并在四核上公平调度。
在四个线程之间分配十个作业的任务和仅利用四个CPU的任务(我这里使用CPU作为核心的同义词 为了简单起见)你的十个工作有点不同。
四个线程
将线程数限制为四个并不能保证它们会坚持使用四个 CPU 并且不会使用其他线程。 OS 可以根据需要在所有可用的 CPU 之间随机播放您的线程。您唯一可以保证的是,您的程序将无法利用所有 CPU 资源的 50% 以上(假设您有八个 CPU)。
但您不太可能设法利用这 50%。尽管您的作业主要是面向 CPU 的,但它们仍然可能需要不时地读取和写入内存。当一个线程在这样的 readings/writings 上错过了缓存并等待数据被传送到处理器时,这个处理器会暂停该线程并可以在另一个线程中做一些工作。在您的情况下,它将无事可做,只是闲置直到数据到达。因此,您的 CPU 很可能未得到充分利用。
如果你决定采用这种方法,你需要将你的工作分解成小任务并将它们提供给执行者,正如@James Large 所说。您可以将 WorkStealingPool
与四个线程一起使用(正如@Alexey Soshin 所建议的那样),或者创建一个具有十个线程的池并使用 Semaphore
并使用四个许可并将公平设置为 true
。在后一种情况下,您的线程必须使用循环,在每次迭代开始时获取许可并在结束时释放它们。每次迭代将代表一小部分工作。
四个CPUs
有一些机制可以指定特定的 CPU 来处理您的任务。
在 Linux 中的进程级别,您可以使用 special commands 将您的进程绑定到特定的 CPU。这将允许您创建十个线程并让 OS 在四个 CPU 上完成所有平衡。
在线程级别,您可以尝试 Java Affinity library 来自 OpenHFT。它允许在您的 Java 代码中将线程绑定到 CPUs。问题是十个线程不能在没有提醒的情况下被分配到四个CPU之间,所以很难平衡它们。