問題:
for(i = 0; i < threads.length; i++)
threads[i].join();
……というか、これthreads[i + 1]
まで参加できなかった…… threads[i]
。「ラッチ」されたものを除いて、すべてのソリューションにはこの欠如があります。
ここで(まだ)誰もExecutorCompletionServiceについて言及していません。完了順序に従ってスレッド/タスクを結合できます。
public class ExecutorCompletionService<V>
extends Object
implements CompletionService<V>
指定されたCompletionService
を使用してExecutor
タスクを実行する 。このクラスは、送信されたタスクが完了すると、take を使用してアクセス可能なキューに配置されるようにします。このクラスは軽量なので、タスクのグループを処理する際の一時的な使用に適しています。
使用例。
特定の問題に対する一連のソルバーがあり、それぞれが何らかのタイプの Result の値を返し、それらを同時に実行して、null 以外の値を返すそれぞれの結果を何らかのメソッドで処理したいとしますuse(Result r)
。これは次のように記述できます。
void solve(Executor e, Collection<Callable<Result>> solvers) throws InterruptedException, ExecutionException {
CompletionService<Result> cs = new ExecutorCompletionService<>(e);
solvers.forEach(cs::submit);
for (int i = solvers.size(); i > 0; i--) {
Result r = cs.take().get();
if (r != null)
use(r);
}
}
代わりに、一連のタスクの最初の null 以外の結果を使用し、例外が発生したものは無視し、最初のタスクの準備ができたら他のすべてのタスクをキャンセルするとします。
void solve(Executor e, Collection<Callable<Result>> solvers) throws InterruptedException {
CompletionService<Result> cs = new ExecutorCompletionService<>(e);
int n = solvers.size();
List<Future<Result>> futures = new ArrayList<>(n);
Result result = null;
try {
solvers.forEach(solver -> futures.add(cs.submit(solver)));
for (int i = n; i > 0; i--) {
try {
Result r = cs.take().get();
if (r != null) {
result = r;
break;
}
} catch (ExecutionException ignore) {}
}
} finally {
futures.forEach(future -> future.cancel(true));
}
if (result != null)
use(result);
}
以降: 1.5 (!)
(例 1 の) 非同期も仮定するとuse(r)
、大きな利点がありました。#