假设您有G
个n x m
个单元格,其中n
和m
很大。
此外,假设我们有许多任务,其中每个任务属于G中的单个单元,并且应该并行执行(在线程池或其他资源池中)。
但是,属于同一个单元格的任务必须按顺序完成,也就是说,它应该等待同一个单元格中的上一个任务完成。
我该如何解决这个问题? 我搜索并使用了几个线程池(Executors,Thread),但没有运气。
最低工作范例
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MWE {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(16);
Random r = new Random();
for (int i = 0; i < 10000; i++) {
int nx = r.nextInt(10);
int ny = r.nextInt(10);
Runnable task = new Runnable() {
public void run() {
try {
System.out.println("Task is running");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
threadPool.submit(new Thread(task)); // Should use nx,ny here somehow
}
}
}
答案 0 :(得分:1)
如果我说得对,你想在Y队列中执行X任务(X非常大)(Y远小于X)。
Java 8具有CompletableFuture
类,表示(异步)计算。基本上,它是Java Promise的实现。以下是如何组织计算链(省略泛型类型):
// start the queue with a "completed" task
CompletableFuture queue = CompletableFuture.completedFuture(null);
// append a first task to the queue
queue = queue.thenRunAsync(() -> System.out.println("first task running"));
// append a second task to the queue
queue = queue.thenRunAsync(() -> System.out.println("second task running"));
// ... and so on
使用thenRunAsync(Runnable)
时,将使用线程池执行任务(还有其他可能性 - 请参阅API docs)。您也可以提供自己的线程池。
您可以创建Y这样的链(可能在某些表中保留对它们的引用)。
答案 1 :(得分:1)
这是像Java世界中的Akka这样的系统有意义。如果X和Y都很大,你可能想看看使用消息传递机制处理它们而不是将它们捆绑在一大堆回调和期货中。一个演员有待完成的任务列表,并交给一个单元格,演员最终会计算结果并坚持下去。如果中间步骤失败了,那就不是世界末日。
答案 2 :(得分:1)
具有synchronized块的回调机制可以在此处高效工作。
我之前回答过类似的问题here。
有一些限制(参见链接的答案),但它足以简单地跟踪正在发生的事情(良好的可维护性)。
我已经调整了源代码,并使其更有效地适用于大多数任务将并行执行的情况
(因为n
和m
很大),但有时必须是连续的(当任务是针对网格G
中的同一点时)。
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
// Adapted from https://stackoverflow.com/a/33113200/3080094
public class GridTaskExecutor {
public static void main(String[] args) {
final int maxTasks = 10_000;
final CountDownLatch tasksDone = new CountDownLatch(maxTasks);
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(16);
try {
GridTaskExecutor gte = new GridTaskExecutor(executor);
Random r = new Random();
for (int i = 0; i < maxTasks; i++) {
final int nx = r.nextInt(10);
final int ny = r.nextInt(10);
Runnable task = new Runnable() {
public void run() {
try {
// System.out.println("Task " + nx + " / " + ny + " is running");
Thread.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
tasksDone.countDown();
}
}
};
gte.addTask(task, nx, ny);
}
tasksDone.await();
System.out.println("All tasks done, task points remaining: " + gte.size());
} catch (Exception e) {
e.printStackTrace();
} finally {
executor.shutdownNow();
}
}
private final Executor executor;
private final Map<Long, List<CallbackPointTask>> tasksWaiting = new HashMap<>();
// make lock fair so that adding and removing tasks is balanced.
private final ReentrantLock lock = new ReentrantLock(true);
public GridTaskExecutor(Executor executor) {
this.executor = executor;
}
public void addTask(Runnable r, int x, int y) {
Long point = toPoint(x, y);
CallbackPointTask pr = new CallbackPointTask(point, r);
boolean runNow = false;
lock.lock();
try {
List<CallbackPointTask> pointTasks = tasksWaiting.get(point);
if (pointTasks == null) {
if (tasksWaiting.containsKey(point)) {
pointTasks = new LinkedList<CallbackPointTask>();
pointTasks.add(pr);
tasksWaiting.put(point, pointTasks);
} else {
tasksWaiting.put(point, null);
runNow = true;
}
} else {
pointTasks.add(pr);
}
} finally {
lock.unlock();
}
if (runNow) {
executor.execute(pr);
}
}
private void taskCompleted(Long point) {
lock.lock();
try {
List<CallbackPointTask> pointTasks = tasksWaiting.get(point);
if (pointTasks == null || pointTasks.isEmpty()) {
tasksWaiting.remove(point);
} else {
System.out.println(Arrays.toString(fromPoint(point)) + " executing task " + pointTasks.size());
executor.execute(pointTasks.remove(0));
}
} finally {
lock.unlock();
}
}
// for a general callback-task, see https://stackoverflow.com/a/826283/3080094
private class CallbackPointTask implements Runnable {
final Long point;
final Runnable original;
CallbackPointTask(Long point, Runnable original) {
this.point = point;
this.original = original;
}
@Override
public void run() {
try {
original.run();
} finally {
taskCompleted(point);
}
}
}
/** Amount of points with tasks. */
public int size() {
int l = 0;
lock.lock();
try {
l = tasksWaiting.size();
} finally {
lock.unlock();
}
return l;
}
// https://stackoverflow.com/a/12772968/3080094
public static long toPoint(int x, int y) {
return (((long)x) << 32) | (y & 0xffffffffL);
}
public static int[] fromPoint(long p) {
return new int[] {(int)(p >> 32), (int)p };
}
}
答案 3 :(得分:1)
您可以创建 n Executors.newFixedThreadPool(1)
的列表。
然后使用哈希函数提交到相应的线程。
例如threadPool[key%n].submit(new Thread(task))
。
答案 4 :(得分:0)
下面给出的 Scala 示例演示了如何并行执行映射中的键以及如何串行执行键的值。如果您想在 Java 中尝试,请将其更改为 Java 语法(Scala 使用 JVM 库)。基本上将任务链接起来,让它们按顺序执行。
useEffect