GPars - 提前终止并行收集的正确方法

时间:2012-12-12 23:12:08

标签: gpars

终止并行集合的最佳方法是什么(在其中一个线程引发的异常或用户发起的中断的情况下)?

在任何一种情况下,我都可以轻松设置一些标志,并在循环的顶部进行检查。但是如果我在集合中有10,000件物品,我宁愿告诉他们将这些物品送进ForkJoinPool以停止喂它们。让5或20左右已经开始完成,但不要再开始了。 / p>

以下是您可以插入以查看我的意思的示例。如果我模拟其中一个线程抛出异常,则看起来集合停止处理,因为线程计数的第一次打印(在'A:'消息处)通常非常小(大约5个)。但是,通过在GParsPool.withPool(在'B:')之外打印计数,我可以看到它们确实在运行(总是全部为100):

void doIt() {
    AtomicInteger threadCounter = new AtomicInteger(0)
    AtomicInteger threadCounterEnd = new AtomicInteger(0)

    try {
        GParsPool.withPool { pool ->

            try {
                (1..100).eachParallel {
                    try {
                        if (threadCounter.incrementAndGet() == 1) {
                            throw new RuntimeException('planet blew up!')
                        }

                        // Do some long work
                        Integer counter=0
                        (1..1000000).each() {counter++}

                        // Flag if we went all the way through
                        threadCounterEnd.incrementAndGet()
                    } catch (Exception exc) {
                        //pool.shutdownNow()
                        throw new RuntimeException(exc)
                    }
                }
            } catch (Exception exc) {
                println 'caught exception'
                //pool.shutdownNow()
                println "A: threads launched was ${threadCounter}, ended was ${threadCounterEnd}"
                throw new RuntimeException(exc)
            }
        }
    } catch (Exception exc) {
        exc.printStackTrace()
    }

    println "B: threads launched was ${threadCounter}, ended was ${threadCounterEnd}"
}

如果我在eachParallel中使用pool.shutdown(),则没有任何影响。如果我使用pool.shutdownNow(),它会像我想要的那样结束处理,但会抛出一个CancellationException,它会掩盖我想要的真正异常。我可以将“真实”异常存储在变量中以供以后访问,但我不得不怀疑是否有更好的方法来告诉并行集合干净地停止。

1 个答案:

答案 0 :(得分:2)

ATM没有这样的机制。由于池无法区分您的任务和其他潜在计算的任务,因此它没有机制来阻止线程读取任务队列。如果您知道自己是线程池的唯一用户,则可以按照建议使用shutdownNow()(具有上述结果)。

我觉得您也知道另一个选项是直接正确捕获任务中的异常并设置共享原子标志以指示其他任务立即退出。