ExecutorService, 모든 작업이 완료 될 때까지 기다리는 방법
모든 작업이 ExecutorService
완료 될 때까지 기다리는 가장 간단한 방법은 무엇입니까 ? 내 작업은 주로 계산 작업이므로 각 코어마다 하나씩 많은 수의 작업을 실행하고 싶습니다. 지금 내 설정은 다음과 같습니다.
ExecutorService es = Executors.newFixedThreadPool(2);
for (DataTable singleTable : uniquePhrases) {
es.execute(new ComputeDTask(singleTable));
}
try{
es.wait();
}
catch (InterruptedException e){
e.printStackTrace();
}
ComputeDTask
실행 가능을 구현합니다. 이 올바르게 작업을 실행하는 것처럼 보이지만 코드에 충돌 wait()
과 함께 IllegalMonitorStateException
. 나는 장난감 예제를 가지고 놀았고 작동하는 것처럼 보였기 때문에 이상합니다.
uniquePhrases
수만 개의 요소를 포함합니다. 다른 방법을 사용해야합니까? 가능한 한 간단한 것을 찾고 있습니다
가장 간단한 방법은 ExecutorService.invokeAll()
하나의 라이너로 원하는 것을 사용 하는 것입니다. 당신의 말로, 당신은 ComputeDTask
구현 하기 위해 수정하거나 줄 바꿈해야 Callable<>
합니다. 아마도 앱에는의 의미있는 구현이 Callable.call()
있지만를 사용하지 않는 경우 랩핑하는 방법이 Executors.callable()
있습니다.
ExecutorService es = Executors.newFixedThreadPool(2);
List<Callable<Object>> todo = new ArrayList<Callable<Object>>(singleTable.size());
for (DataTable singleTable: uniquePhrases) {
todo.add(Executors.callable(new ComputeDTask(singleTable)));
}
List<Future<Object>> answers = es.invokeAll(todo);
다른 사람들이 지적했듯이 invokeAll()
적절한 경우 시간 초과 버전을 사용할 수 있습니다 . 이 예에서는 null을 반환하는 s answers
뭉치를 포함합니다 Future
(의 정의 참조 Executors.callable()
. 아마 당신이하고 싶은 것은 약간의 리팩토링이므로 유용한 답변을 얻거나 기본에 대한 참조를 ComputeDTask
얻을 수 있지만 당신의 모범에서 말하지 마십시오.
확실 invokeAll()
하지 않으면 모든 작업이 완료 될 때까지 반환되지 않습니다. (즉, 컬렉션 Future
의 모든 s가 요청 answers
되면보고 .isDone()
합니다.) 이렇게하면 모든 수동 종료, awaitTermination 등을 피할 수 있으며 ExecutorService
원하는 경우 여러주기 동안 깔끔하게 재사용 할 수 있습니다 .
SO에 대한 몇 가지 관련 질문이 있습니다.
이 중 어느 것도 엄격에 포인트 질문에 대한 없지만, 그들은 사람들이 생각하는 방법에 대한 색의 약간 제공 할 Executor
/가 ExecutorService
사용되어야한다을.
모든 작업이 완료 될 때까지 기다리려면 shutdown
대신 방법을 사용하십시오 wait
. 그런 다음로 수행하십시오 awaitTermination
.
또한 Runtime.availableProcessors
스레드 풀 수를 올바르게 초기화 할 수 있도록 하드웨어 스레드 수를 얻는 데 사용할 수 있습니다 .
ExecutorService
완료 할 때까지의 모든 작업을 기다리는 것이 정확히 목표가 아니라 특정 작업 이 완료 될 때까지 기다리는 경우에는 CompletionService
구체적으로 ExecutorCompletionService
.
아이디어는이 만드는 것입니다 ExecutorCompletionService
귀하의 포장 Executor
, 제출 일부 알려진 번호를 관통 작업을 CompletionService
한 후, 그 그릴 같은 번호 로부터 결과의 완성 대기열 중 하나를 사용하여 take()
(이 블록) 또는 poll()
(하지 않음). 제출 한 작업에 해당하는 모든 예상 결과를 가져 오면 모두 완료된 것입니다.
인터페이스에서 명확하지 않기 때문에 한 번 더 설명하겠습니다. CompletionService
몇 가지를 그려야하는지 알기 위해서는 몇 가지를 입력 해야하는지 알아야합니다. 이것은 특히 take()
메소드 와 관련이 있습니다. 한 번 너무 많이 호출하면 다른 스레드가 다른 작업을 동일한 작업에 제출 할 때까지 호출 스레드를 차단합니다 CompletionService
.
Java Concurrency in Practice 책에서 사용하는 방법을 보여주는 몇 가지 예가CompletionService
있습니다 .
당신이 실행을 완료하기 위해 실행 프로그램 서비스를 대기하는 경우, 전화를 shutdown()
한 후, awaitTermination (단위 UNITTYPE) 예 awaitTermination(1, MINUTE)
. ExecutorService는 자체 모니터를 차단하지 않으므로 사용할 수 없습니다 wait
.
특정 간격으로 작업이 완료 될 때까지 기다릴 수 있습니다.
int maxSecondsPerComputeDTask = 20;
try {
while (!es.awaitTermination(uniquePhrases.size() * maxSecondsPerComputeDTask, TimeUnit.SECONDS)) {
// consider giving up with a 'break' statement under certain conditions
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
또는 ExecutorService를 사용할 수 있습니다 . ( Runnable )을 제출 하고 반환 하는 Future 객체를 수집하고 각각 get () 을 호출 하여 완료 될 때까지 기다립니다.
ExecutorService es = Executors.newFixedThreadPool(2);
Collection<Future<?>> futures = new LinkedList<<Future<?>>();
for (DataTable singleTable : uniquePhrases) {
futures.add(es.submit(new ComputeDTask(singleTable)));
}
for (Future<?> future : futures) {
try {
future.get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
올바르게 처리하려면 InterruptedException 이 매우 중요합니다. 그것은 당신이나 당신의 라이브러리 사용자가 긴 프로세스를 안전하게 종료 할 수있게합니다.
그냥 사용
latch = new CountDownLatch(noThreads)
각 실에서
latch.countDown();
그리고 장벽으로
latch.await();
IllegalMonitorStateException의 근본 원인 :
스레드가 지정된 모니터를 소유하지 않고 오브젝트의 모니터에서 대기하려고 시도했거나 오브젝트의 모니터에서 대기중인 다른 스레드에게 알리려고 시도했음을 표시합니다.
코드에서 잠금을 소유하지 않고 ExecutorService에서 wait ()를 호출했습니다.
아래 코드는 수정됩니다 IllegalMonitorStateException
try
{
synchronized(es){
es.wait(); // Add some condition before you call wait()
}
}
Follow one of below approaches to wait for completion of all tasks, which have been submitted to ExecutorService
.
Iterate through all
Future
tasks fromsubmit
onExecutorService
and check the status with blocking callget()
onFuture
objectUsing invokeAll on
ExecutorService
Using CountDownLatch
Using ForkJoinPool or newWorkStealingPool of
Executors
(since java 8)Shutdown the pool as recommended in oracle documentation page
void shutdownAndAwaitTermination(ExecutorService pool) { pool.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being cancelled if (!pool.awaitTermination(60, TimeUnit.SECONDS)) System.err.println("Pool did not terminate"); } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted pool.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); }
If you want to gracefully wait for all tasks for completion when you are using option 5 instead of options 1 to 4, change
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
to
a
while(condition)
which checks for every 1 minute.
You can use ExecutorService.invokeAll
method, It will execute all task and wait till all threads finished their task.
Here is complete javadoc
You can also user overloaded version of this method to specify the timeout.
Here is sample code with ExecutorService.invokeAll
public class Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService service = Executors.newFixedThreadPool(3);
List<Callable<String>> taskList = new ArrayList<>();
taskList.add(new Task1());
taskList.add(new Task2());
List<Future<String>> results = service.invokeAll(taskList);
for (Future<String> f : results) {
System.out.println(f.get());
}
}
}
class Task1 implements Callable<String> {
@Override
public String call() throws Exception {
try {
Thread.sleep(2000);
return "Task 1 done";
} catch (Exception e) {
e.printStackTrace();
return " error in task1";
}
}
}
class Task2 implements Callable<String> {
@Override
public String call() throws Exception {
try {
Thread.sleep(3000);
return "Task 2 done";
} catch (Exception e) {
e.printStackTrace();
return " error in task2";
}
}
}
I also have the situation that I have a set of documents to be crawled. I start with an initial "seed" document which should be processed, that document contains links to other documents which should also be processed, and so on.
In my main program, I just want to write something like the following, where Crawler
controls a bunch of threads.
Crawler c = new Crawler();
c.schedule(seedDocument);
c.waitUntilCompletion()
The same situation would happen if I wanted to navigate a tree; i would pop in the root node, the processor for each node would add children to the queue as necessary, and a bunch of threads would process all the nodes in the tree, until there were no more.
I couldn't find anything in the JVM which I thought was a bit surprising. So I wrote a class AutoStopThreadPool
which one can either use directly or subclass to add methods suitable for the domain, e.g. schedule(Document)
. Hope it helps!
AutoStopThreadPool Javadoc | Download
Add all threads in collection and submit it using invokeAll
. If you can use invokeAll
method of ExecutorService
, JVM won’t proceed to next line until all threads are complete.
Here there is a good example: invokeAll via ExecutorService
Submit your tasks into the Runner and then wait calling the method waitTillDone() like this:
Runner runner = Runner.runner(2);
for (DataTable singleTable : uniquePhrases) {
runner.run(new ComputeDTask(singleTable));
}
// blocks until all tasks are finished (or failed)
runner.waitTillDone();
runner.shutdown();
To use it add this gradle/maven dependency: 'com.github.matejtymes:javafixes:1.0'
For more details look here: https://github.com/MatejTymes/JavaFixes or here: http://matejtymes.blogspot.com/2016/04/executor-that-notifies-you-when-task.html
A simple alternative to this is to use threads along with join. Refer : Joining Threads
I will just wait for the executor to terminate with a specified timeout that you think it is suitable for the tasks to complete.
try {
//do stuff here
exe.execute(thread);
} finally {
exe.shutdown();
}
boolean result = exe.awaitTermination(4, TimeUnit.HOURS);
if (!result)
{
LOGGER.error("It took more than 4 hour for the executor to stop, this shouldn't be the normal behaviour.");
}
Sounds like you need ForkJoinPool
and use the global pool to execute tasks.
public static void main(String[] args) {
// the default `commonPool` should be sufficient for many cases.
ForkJoinPool pool = ForkJoinPool.commonPool();
// The root of your task that may spawn other tasks.
// Make sure it submits the additional tasks to the same executor that it is in.
Runnable rootTask = new YourTask(pool);
pool.execute(rootTask);
pool.awaitQuiescence(...);
// that's it.
}
The beauty is in pool.awaitQuiescence
where the method will block utilize the caller's thread to execute its tasks and then return when it is really empty.
참고URL : https://stackoverflow.com/questions/3269445/executorservice-how-to-wait-for-all-tasks-to-finish
'Programming' 카테고리의 다른 글
목록 이해에 'else'를 사용할 수 있습니까? (0) | 2020.05.16 |
---|---|
XDocument를 XmlDocument로 또는 그 반대로 변환 (0) | 2020.05.16 |
Microsoft Excel에서 .csv 파일의 분음 부호를 표시합니까? (0) | 2020.05.16 |
C는 그렇게 어렵지 않습니다 : void (* (* f []) ()) () (0) | 2020.05.16 |
마지막 git add를 어떻게 취소 할 수 있습니까? (0) | 2020.05.16 |