[英]Issue with timeouts when using ExecutorService
我有幾十萬個Callable
對象的列表。 每次運行時,它都會根據提供給它的值執行可能冗長的計算。 因此,我想異步運行每個任務(最好通過使用某種Executor),並在30秒后檢索每個計算的結果,以取消未及時完成的任務。 (獲得的值在其他地方使用。)
到目前為止,這是我的實現方式:
private void process() {
class Runner implements Callable<BigDecimal> {
final int x, y;
Runner(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public BigDecimal call() {
BigDecimal gWidth = xMax.subtract(xMin), gHeight = yMax.subtract(yMin);
BigDecimal gy = gHeight.multiply(BigDecimal.valueOf(-y)).divide(BigDecimal.valueOf(height)).add(yMax);
BigDecimal gx = gWidth.multiply(BigDecimal.valueOf(x)).divide(BigDecimal.valueOf(width)).add(xMin);
// The calculation begins when this method is called
BigDecimal result = calculateAt(gx, gy);
return result;
}
}
ExecutorService exec = Executors.newCachedThreadPool();
List<Runner> runners = new ArrayList<>();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
runners.add(new Runner(x, y));
}
}
try {
List<Future<BigDecimal>> results = exec.invokeAll(runners, 30, TimeUnit.SECONDS);
for (Future<BigDecimal> future : results) {
// Check if the future's task was cancelled and process the results
}
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
exec.shutdown();
}
// Extra variables and methods
BigDecimal xMin = BigDecimal.valueOf(-7),
xMax = BigDecimal.valueOf(7),
yMin = BigDecimal.valueOf(-7),
yMax = BigDecimal.valueOf(7);
int height = 850, width = 850;
private BigDecimal calculateAt(BigDecimal x, BigDecimal y) {
try {
// Just to simulate a potential execution time
Thread.sleep((ThreadLocalRandom.current().nextInt(45) + 1) * 1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return BigDecimal.ONE;
}
ArrayList的runners
存儲每個Callable
執行,然后將其發送給ExecutorService的運行所有任務。 我遇到的問題是任務似乎是同步啟動的,並且在30秒超時后,只有前40或5萬個任務已經完成,而開始執行的任務就少得多。
似乎正在發生的事情是, ExecutorService.invokeAll
方法僅允許30秒的窗口來啟動和結束執行列表中的所有任務。 相反,我需要讓這30秒的窗口以每個任務為基礎開始,即在任務開始后等待30秒才能完成。 invokeAll
似乎沒有執行此操作,至少沒有使用newCachedThreadPool
。 是否有Java庫或其他實現方法?
最簡單的方法是分別調用每個任務,例如在初始化Runner
的循環中。 由於您上面提到的行為與ExecutorService
的JavaDoc文檔兼容,因此沒有任何子類(至少是Java Standard Edition附帶的子類)會有所不同。 實現該行為的獨特功能也不存在。
從#invokeAll(List<? extends Callable>, long, TimeUnit)
的文檔中:
執行給定的任務,並在所有任務完成或超時到期時(以先發生者為准)返回持有其狀態和結果的期貨列表。 Future.isDone()對於返回列表的每個元素為true。 返回時,尚未完成的任務將被取消。 請注意,已完成的任務可能已正常終止,也可能引發異常。 如果在進行此操作時修改了給定的集合,則此方法的結果不確定。
我認為您可以使用CompletableFuture
解決問題。
例如,以下是基於問題片段的代碼:
private static final ExecutorService executor = Executors.newCachedThreadPool();
private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(100);
private static void processAsync() {
List<CompletableFuture<Object>> futureList = IntStream.range(0, height).boxed()
.flatMap(y -> IntStream.range(0, width).boxed().map(x -> new Runner(x, y)))
.map(runner ->
CompletableFuture.anyOf(
CompletableFuture.supplyAsync(runner, executor),
timeout(Duration.ofSeconds(30))
).exceptionally(throwable -> {
// timeout is handled here
return BigDecimal.ZERO;
})
)
.collect(Collectors.toList());
CompletableFuture.allOf(futureList.toArray(new CompletableFuture<?>[0]))
.thenAccept(v -> {
List<BigDecimal> results = futureList.stream()
.map(CompletableFuture::join)
.map(r -> (BigDecimal) r)
.collect(Collectors.toList());
// process final results
BigDecimal sum = results.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println("Final sum: " + sum);
})
.exceptionally(throwable -> {
System.out.println("Failed allOf with " + throwable);
return null;
});
}
private static CompletableFuture<BigDecimal> timeout(Duration duration) {
CompletableFuture<BigDecimal> future = new CompletableFuture<>();
scheduler.schedule(
() -> future.completeExceptionally(new TimeoutException("Timeout " + Thread.currentThread().getName())), duration.toMillis(), MILLISECONDS);
return future;
}
private static class Runner implements Supplier<BigDecimal> {...
這里的主要思想是使用CompletableFuture.anyOf
並將其應用於有用任務的CompletableFuture
和超時任務的CopmpletableFuture
。 通過使用ScheduledExecutorService
和CompletableFuture.completeExceptionally
實現超時。 因此,基本上, anyOf
返回result或TimeoutException
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.