Groovy でマルチスレッドで実行した処理の結果を処理が終了した順序で取得するには

はじめに

ExecutorCompletionService を利用することで、マルチスレッドで実行した処理の結果を処理が終了した順序で取得できる。

ExecutorCompletionService とは

非同期処理の実行と、その結果の取得を分離するためのサービス。

submit メソッドで非同期処理の実行をし、結果の取得は take メソッドでする。
take メソッドは処理が終了した順に処理の結果を返し、終了した処理が存在しない場合は次の処理が終了するまで待機する。

submit の戻り値の Future<V> を使用すると、ExecutorService を使う場合と動作は変わらない。

take の呼び出し回数は submit した回数を超えないように注意が必要。
submit の回数以上に呼び出した場合は、take を実行したスレッドが中断するまで take メソッドが待機する。

import java.util.concurrent.Callable
import java.util.concurrent.ExecutorCompletionService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

def log(status) {
    println "[${Thread.currentThread().name}][${new Date().format('HH:mm:ss.SSS')}] ${status}"
}

def service = Executors.newFixedThreadPool(5)
def completionService = new ExecutorCompletionService(service)

(0..<5).each { i ->
    completionService.submit({
        log '開始'
        Thread.sleep((5 - i) * 1000)
        log '終了'

        "終了 (${Thread.currentThread().name})"
    } as Callable<String>)
}

service.shutdown()

log '待機中'
(0..<5).each { i ->
    // 処理が終了した順に結果が取得可能
    log completionService.take().get()
}

service.awaitTermination(1, TimeUnit.MINUTES)
log '待機完了'

処理が終了した順に結果が取得できている。

[pool-1-thread-3][21:01:11.938] 開始
[pool-1-thread-1][21:01:11.938] 開始
[pool-1-thread-5][21:01:11.938] 開始
[pool-1-thread-4][21:01:11.938] 開始
[pool-1-thread-2][21:01:11.938] 開始
[main][21:01:11.938] 待機中
[pool-1-thread-5][21:01:12.986] 終了
[main][21:01:12.986] 終了 (pool-1-thread-5)
[pool-1-thread-4][21:01:13.988] 終了
[main][21:01:13.988] 終了 (pool-1-thread-4)
[pool-1-thread-3][21:01:14.989] 終了
[main][21:01:14.989] 終了 (pool-1-thread-3)
[pool-1-thread-2][21:01:15.991] 終了
[main][21:01:15.991] 終了 (pool-1-thread-2)
[pool-1-thread-1][21:01:16.992] 終了
[main][21:01:16.992] 終了 (pool-1-thread-1)
[main][21:01:16.992] 待機完了

コメント