gpt4 book ai didi

java - 在 Java 中,如何定义返回多个答案中最佳答案的 Future

转载 作者:塔克拉玛干 更新时间:2023-11-02 20:03:03 26 4
gpt4 key购买 nike

我需要一个 V 类型的值这将必须异步计算。不幸的是,最佳答案可能需要很长时间才能计算出来,所以我有几个其他的值,我会在紧要关头接受它们。我想做的是定义一个 Future<V>我可以调用超时并让它返回当时可用的最佳答案。像这样的东西:

Future<V> theValue = // something involving executor.submit()
// inside here, asynchronous calls to find
V default = // something pretty quick to return
V good = // something that will take longer and might not return in time
V better = // something that will take longest but is the best answer

V v = theValue.get(5, TimeUnit.SECONDS); // now v should contain one of default,
// good, or better, preferring them in the expected order

我确信这是一个相当普遍的模式,但我一直没能找到一个很好的例子。有帮助吗?

最佳答案

在提议的场景中,有三种不同版本的计算(我将它们命名为更好最好)。每个后续版本都会产生一个优于之前版本的结果,但由于复杂性增加,可能需要分别花费更长的时间才能完成。调用者愿意等待任何结果的任意时间量,在这个答案中,我将使用 五 (5) 秒 的值作为该截止时间。

可以通过使用一对 latches 来保持这种严格的偏好顺序,同时避免循环和排队。对于每批相关操作。


基本逻辑

ExecutorService execService = // ... e.g. new ThreadPoolExecutor(...)

<T> T compute(
Callable<? extends T> good,
Callable<? extends T> better,
Callable<? extends T> best) {

RelatedCallables calls = new RelatedCallables(); // new for each batch
Future<T> bestResult = execService.submit(calls.wrap(best)); // first wrapped is primary
Future<T> betterResult = execService.submit(calls.wrap(better));
Future<T> goodResult = execService.submit(calls.wrap(good));

try {
if (!calls.awaitPrimary(5, TimeUnit.SECONDS)) {
calls.awaitAny(); // waits indefinitely, unless THIS thread interrupted
}
// reaching here means at least one of them has a result
if (bestResult.isDone()) return bestResult.get();
if (betterResult.isDone()) return betterResult.get();
return goodResult.get();
}
catch (ExecutionException failedExecution) {
// TODO: handling this is left as an exercise for the reader
return null;
}
catch (InterruptedException interrupted) {
// TODO: handling this is left as an exercise for the reader
return null;
}
finally {
boolean sendInterrupt = true; // or false, depending on your needs
goodResult.cancel(sendInterrupt);
betterResult.cancel(sendInterrupt);
bestResult.cancel(sendInterrupt);
}
}

此解决方案使用辅助类 RelatedCallables(稍后详述)来设置作为 Callable 实例提交的三个计算之间的关系。帮助类将包装每个实例,并将包装器提交给 ExecutorService 以并行执行。在此实现中,最好 Callable 首先 被包装很重要。包装和提交其​​他实例的顺序并不重要。

RelatedCallables 帮助程序上的 awaitPrimaryawaitAny 方法的组合,以及 if 条件, 设置我们的等待和超时策略。如果最佳(主要)结果在指定的超时时间内可用,它会跳过 if block 的内容并直接继续返回结果,这样调用者就不必等待整整五秒钟。

如果 awaitPrimarybest 计算完成之前超时,则它进入条件的 true 分支并无限期地等待 任何计算完成。通常预期(但我不假设)其他计算中的至少一个将在主要等待时间内完成;如果是这样,awaitAny 将立即返回。除了无限期地等待三个计算之一,还可以返回一个预定值 null,或者通过一些相对较小的调整抛出异常。

一旦程序流通过条件 block ,它就会按优先顺序检查每个 Future 并返回第一个指示它已完成的值。另请注意,finally block 会尝试取消任何未决的计算。


辅助(内部)类

static class RelatedCallables {
private final CountDownLatch primaryLatch = new CountDownLatch(1);
private final CountDownLatch anyLatch = new CountDownLatch(1);
private boolean hasPrimary;

void awaitAny() throws InterruptedException {
anyLatch.await();
}

boolean awaitPrimary(long timeout, TimeUnit unit) throws InterruptedException {
return primaryLatch.await(timeout, unit);
}

<T> Callable<T> wrap(final Callable<? extends T> original) {
final boolean isPrimary = !hasPrimary;
hasPrimary = true;

return new Callable<T>() {
@Override
public T call() throws Exception {
try {
return original.call();
}
finally {
anyLatch.countDown();
if (isPrimary) primaryLatch.countDown();
}
}
};
}
}

这是一个相对简单的 Callable 包装器,它链接到包装的实例。其主要目的是在包装实例完成后减少 anyLatch 闩锁计数。这就是 awaitAny 将知道是否由该助手包装的任何可调用项已完成的方式。

还有第二个锁存器,仅与提交给wrap第一个 Callable 一起使用,逻辑上区分主要(或最好的)实例。该实例的包装器递减此单独的锁存器,以便 awaitPrimary 可以在截止时间到期之前完成best 计算的情况下快速超时。

因为 CountDownLatch 不可重复使用,所以每批不同的 Callable 操作都需要一对新的锁存器。在此实现中,这是通过为每个批处理创建 RelatedCallables新实例来实现的。

关于java - 在 Java 中,如何定义返回多个答案中最佳答案的 Future<V>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26182853/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com