3

У меня есть сценарий, когда я должен опросить удаленный сервер, проверяя, завершена ли задание. После этого я делаю другой вызов для получения результата.Как использовать ExecutorService для опроса до получения результата

Первоначально я полагал, что я должен использовать SingleThreadScheduledExecutor с scheduleWithFixedDelay для опроса:

ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 
ScheduledFuture future = executor.scheduleWithFixedDelay(() -> poll(jobId), 0, 10, TimeUnit.SECONDS); 

public void poll(String jobId) { 
    boolean jobDone = remoteServer.isJobDone(jobId); 
    if (jobDone) { 
     retrieveJobResult(jobId); 
    } 
} 

Но так как я могу только обеспечить RunnablescheduleWithFixedDelay в которой ничего не может вернуться, я не понимаю, когда future будет быть полным, если вообще когда-либо. Что означает вызов future.get()? Какого результата я жду?

В первый раз, когда я обнаружил, что удаленная задача выполнена, я хочу выполнить другой удаленный вызов и установить его результат как значение future. Я полагал, что я мог бы использовать CompletableFuture для этого, что я хотел бы направить на мой poll метод, который в свою очередь направляет его к моему retrieveTask метод, который в конечном итоге завершить его:

CompletableFuture<Object> result = new CompletableFuture<Object>(); 
ScheduledFuture future = executor.scheduleWithFixedDelay(() -> poll(jobId, result), 0, 10, TimeUnit.SECONDS); 

public void poll(String jobId, CompletableFuture<Object> result) { 
    boolean jobDone = remoteServer.isJobDone(jobId); 
    if (jobDone) { 
     retrieveJobResult(jobId, result); 
    } 
} 

public void retrieveJobResult(String jobId, CompletableFuture<Object> result) { 
    Object remoteResult = remoteServer.getJobResult(jobId); 
    result.complete(remoteResult); 
} 

Но это имеет массу проблем. Например, CompletableFuture даже не предназначен для такого использования. Вместо этого я должен был делать CompletableFuture.supplyAsync(() -> poll(jobId)) Я думаю, но как бы я тогда правильно выключил executor и отменил future, который он вернул, когда мой CompletableFuture отменен/завершен? Похоже, что опрос должен реализовываться совершенно по-другому.

+1

Вы также можете представить, что (вызываемые объекты возвращаемых результаты): https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Callable. html – Thilo

+1

@Thilo Только для одноразовых задач, а не с расписаниемWithFixedDelay или \t scheduleAtFixedRate, поэтому опрос отключен – kaqqao

+0

@Thilo Я не думаю, что 'scheduleWithFixedDelay' когда-нибудь получит' Callable'. – HuStmpHrrr

ответ

1

Я думаю CompletableFutures это прекрасный способ сделать это:

ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 

private void run() { 
    final Object jobResult = pollForCompletion("jobId1") 
      .thenRun(jobId -> remoteServer.getJobResult(jobId)) 
      .get(); 

} 

private CompletableFuture<String> pollForCompletion(String jobId) { 
    CompletableFuture<String> completionFuture = new CompletableFuture<>(); 
    final ScheduledFuture<Void> checkFuture = executor.scheduleAtFixedRate(() -> { 
     if (remoteServer.isJobDone(jobId)) { 
      completionFuture.complete(jobId); 
     } 
    }, 0, 10, TimeUnit.SECONDS); 
    completionFuture.whenComplete((result, thrown) -> { 
     checkFuture.cancel(true); 
    }); 
    return completionFuture; 
} 
+0

Спасибо! Наконец, я понимаю, как многократные шаги должны быть объединены. Я пропустил это все время. Пойдемте попробовать. – kaqqao

+0

Что такое правильный способ проверить внутри 'whenComplete', если будущее было отменено? Я могу только придумать 'брошенный экземпляр ExpressionExpression' – kaqqao

+0

Я не вспоминаю о моей голове; CancellationException _may_ будет завершено в ExecutionException. (Таким образом, вам нужно проверить 'thrown.getCause() instanceof CancellationException'.) Однако в моем примере кода нет ничего, что могло бы привести к тому, что' pollForCompletion' CompletableFuture будет отменен. Можете ли вы обновить свой вопрос с помощью своего нового кода? –

1

мне кажется, вы больше обеспокоены некоторыми стилистическими проблемами, чем любые другие. в java 8, CompletableFuture имеет 2 роли: одно традиционное будущее, которое дает асинхронный источник для выполнения задачи и запроса статуса; другое - это то, что мы обычно называем обещанием. обещание, если вы еще не знаете, можно считать строителем будущего и его источником завершения. поэтому в этом случае требуется интуитивно обещание, которое является точным случаем, который вы используете здесь. примеры, о которых вы беспокоитесь, - это то, что вводит вам первое использование, но не обещание.

Принимая это, вам должно быть проще начать работу с вашей реальной проблемой. я думаю, что обещание должно состоять из двух ролей, одно - уведомлять о завершении вашей задачи опроса, а другое - отменить запланированное задание по завершении. здесь должна быть окончательным решением:

public CompletableFuture<Object> pollTask(int jobId) { 
    CompletableFuture<Object> fut = new CompletableFuture<>(); 
    ScheduledFuture<?> sfuture = executor.scheduleWithFixedDelay(() -> _poll(jobId, fut), 0, 10, TimeUnit.SECONDS); 
    fut.thenAccept(ignore -> sfuture.cancel(false)); 
    return fut; 
} 

private void _poll(int jobId, CompletableFuture<Object> fut) { 
    // whatever polls 
    if (isDone) { 
     fut.complete(yourResult); 
    } 
} 
+0

А теперь я понимаю, как нужно сочетать шаги с «Будущим». Это до сих пор ускользало от меня. Спасибо, это полезно. Я сейчас попробую. – kaqqao

+0

Все еще есть проблемы с этим кодом, но это [другой вопрос] (http://stackoverflow.com/questions/40258146/completablefuturewhencomplete-not-called-if-thenapply-is-used) из этого. Пожалуйста, ознакомьтесь с новым вопросом, если вы все еще хотите помочь мне. Еще раз спасибо. – kaqqao

Смежные вопросы