タイムアウトの問題ExecutorServiceのを使用して

TNT:

私は数十万のリスト持っているCallableオブジェクトを。それぞれが実行されると、それに与えられた値に基づいて、潜在的に長い計算を行います。このため、私は、(好ましくはエグゼキュータのいくつかの種類を使用して)非同期で各タスクを実行し、時間内に完了しないものをキャンセルし、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少なくともないで、これを実行していないようですnewCachedThreadPoolJavaライブラリやない実装の他の方法はありますか?

Oleksii Zghurskyi:

私はあなたの問題を解決することができると思います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タイムアウトタスクの。タイムアウトを使用することによって達成されるScheduledExecutorServiceCompletableFuture.completeExceptionallyだから、基本的には、anyOfリターンはどちらか結果かTimeoutException

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=329979&siteId=1