在超时后执行 returns 的长时间计算
Performing a long calculation that returns after a timeout
我想使用迭代加深执行搜索,这意味着每次我这样做时,我都会更深入并且需要更长的时间。有时间限制(2 秒)以获得最佳结果。根据我的研究,执行此操作的最佳方法是使用 ExecutorService、Future 并在时间用完时中断它。这是我目前拥有的:
在我的主要功能中:
ExecutorService service = Executors.newSingleThreadExecutor();
ab = new AB();
Future<Integer> f = service.submit(ab);
Integer x = 0;
try {
x = f.get(1990, TimeUnit.MILLISECONDS);
}
catch(TimeoutException e) {
System.out.println("cancelling future");
f.cancel(true);
}
catch(Exception e) {
throw new RuntimeException(e);
}
finally {
service.shutdown();
}
System.out.println(x);
和可调用对象:
public class AB implements Callable<Integer> {
public AB() {}
public Integer call() throws Exception {
Integer x = 0;
int i = 0;
while (!Thread.interrupted()) {
x = doLongComputation(i);
i++;
}
return x;
}
}
我有两个问题:
- doLongComputation() 未被中断,程序仅在完成工作后检查 Thread.interrupted() 是否为真。我是否需要在 doLongComputation() 中检查线程是否已被中断?
- 即使我摆脱了 doLongComputation(),主要方法也没有接收到 x 的值。我如何确保我的程序等待 Callable 到 "clean up" 和 return 迄今为止最好的 x?
回答第 1 部分:是的,您需要让您的长任务检查中断标志。打断需要被打断任务的配合
另外你应该使用Thread.currentThread().isInterrupted()
除非你特别想清除中断标志。抛出(或重新抛出)InterruptedException 的代码使用 Thread#interrupted
作为检查标志和清除标志的便捷方式,当您编写 Runnable 或 Callable 时,这通常不是您想要的。
现在回答第 2 部分:取消不是您想要的。
使用取消来停止计算,return 中间结果不起作用,一旦取消未来,就无法从 get 方法中检索 return 值。您可以做的是将计算的每个细化作为自己的任务,这样您就可以提交一个任务,获得结果,然后使用该结果作为起点提交下一个任务,同时保存最新的结果。
这是我想出的一个例子来证明这一点,使用牛顿法计算平方根的逐次逼近。每次迭代都是一个单独的任务,在前一个任务完成时提交(使用前一个任务的近似值):
import java.util.concurrent.*;
import java.math.*;
public class IterativeCalculation {
static class SqrtResult {
public final BigDecimal value;
public final Future<SqrtResult> next;
public SqrtResult(BigDecimal value, Future<SqrtResult> next) {
this.value = value;
this.next = next;
}
}
static class SqrtIteration implements Callable<SqrtResult> {
private final BigDecimal x;
private final BigDecimal guess;
private final ExecutorService xs;
public SqrtIteration(BigDecimal x, BigDecimal guess, ExecutorService xs) {
this.x = x;
this.guess = guess;
this.xs = xs;
}
public SqrtResult call() {
BigDecimal nextGuess = guess.subtract(guess.pow(2).subtract(x).divide(new BigDecimal(2).multiply(guess), RoundingMode.HALF_EVEN));
return new SqrtResult(nextGuess, xs.submit(new SqrtIteration(x, nextGuess, xs)));
}
}
public static void main(String[] args) throws Exception {
long timeLimit = 10000L;
ExecutorService xs = Executors.newSingleThreadExecutor();
try {
long startTime = System.currentTimeMillis();
Future<SqrtResult> f = xs.submit(new SqrtIteration(new BigDecimal("612.00"), new BigDecimal("10.00"), xs));
for (int i = 0; System.currentTimeMillis() - startTime < timeLimit; i++) {
f = f.get().next;
System.out.println("iteration=" + i + ", value=" + f.get().value);
}
f.cancel(true);
} finally {
xs.shutdown();
}
}
}
我想使用迭代加深执行搜索,这意味着每次我这样做时,我都会更深入并且需要更长的时间。有时间限制(2 秒)以获得最佳结果。根据我的研究,执行此操作的最佳方法是使用 ExecutorService、Future 并在时间用完时中断它。这是我目前拥有的:
在我的主要功能中:
ExecutorService service = Executors.newSingleThreadExecutor();
ab = new AB();
Future<Integer> f = service.submit(ab);
Integer x = 0;
try {
x = f.get(1990, TimeUnit.MILLISECONDS);
}
catch(TimeoutException e) {
System.out.println("cancelling future");
f.cancel(true);
}
catch(Exception e) {
throw new RuntimeException(e);
}
finally {
service.shutdown();
}
System.out.println(x);
和可调用对象:
public class AB implements Callable<Integer> {
public AB() {}
public Integer call() throws Exception {
Integer x = 0;
int i = 0;
while (!Thread.interrupted()) {
x = doLongComputation(i);
i++;
}
return x;
}
}
我有两个问题:
- doLongComputation() 未被中断,程序仅在完成工作后检查 Thread.interrupted() 是否为真。我是否需要在 doLongComputation() 中检查线程是否已被中断?
- 即使我摆脱了 doLongComputation(),主要方法也没有接收到 x 的值。我如何确保我的程序等待 Callable 到 "clean up" 和 return 迄今为止最好的 x?
回答第 1 部分:是的,您需要让您的长任务检查中断标志。打断需要被打断任务的配合
另外你应该使用Thread.currentThread().isInterrupted()
除非你特别想清除中断标志。抛出(或重新抛出)InterruptedException 的代码使用 Thread#interrupted
作为检查标志和清除标志的便捷方式,当您编写 Runnable 或 Callable 时,这通常不是您想要的。
现在回答第 2 部分:取消不是您想要的。
使用取消来停止计算,return 中间结果不起作用,一旦取消未来,就无法从 get 方法中检索 return 值。您可以做的是将计算的每个细化作为自己的任务,这样您就可以提交一个任务,获得结果,然后使用该结果作为起点提交下一个任务,同时保存最新的结果。
这是我想出的一个例子来证明这一点,使用牛顿法计算平方根的逐次逼近。每次迭代都是一个单独的任务,在前一个任务完成时提交(使用前一个任务的近似值):
import java.util.concurrent.*;
import java.math.*;
public class IterativeCalculation {
static class SqrtResult {
public final BigDecimal value;
public final Future<SqrtResult> next;
public SqrtResult(BigDecimal value, Future<SqrtResult> next) {
this.value = value;
this.next = next;
}
}
static class SqrtIteration implements Callable<SqrtResult> {
private final BigDecimal x;
private final BigDecimal guess;
private final ExecutorService xs;
public SqrtIteration(BigDecimal x, BigDecimal guess, ExecutorService xs) {
this.x = x;
this.guess = guess;
this.xs = xs;
}
public SqrtResult call() {
BigDecimal nextGuess = guess.subtract(guess.pow(2).subtract(x).divide(new BigDecimal(2).multiply(guess), RoundingMode.HALF_EVEN));
return new SqrtResult(nextGuess, xs.submit(new SqrtIteration(x, nextGuess, xs)));
}
}
public static void main(String[] args) throws Exception {
long timeLimit = 10000L;
ExecutorService xs = Executors.newSingleThreadExecutor();
try {
long startTime = System.currentTimeMillis();
Future<SqrtResult> f = xs.submit(new SqrtIteration(new BigDecimal("612.00"), new BigDecimal("10.00"), xs));
for (int i = 0; System.currentTimeMillis() - startTime < timeLimit; i++) {
f = f.get().next;
System.out.println("iteration=" + i + ", value=" + f.get().value);
}
f.cancel(true);
} finally {
xs.shutdown();
}
}
}