抽象
在谈论分层问题时,请考虑快速排序、归并排序、文件系统或一般树导航等。
-
Fork/Join 算法实质上将手头的任务分解成多个较小的子任务,并递归地对每个子任务应用相同的算法。
-
一旦子任务足够小,就可以直接解决。
-
所有子任务的解决方案被组合起来以解决它们的父任务,这反过来有助于解决其自身的父任务。
JSR-166y 库存在一些粗糙的边缘,可能会伤害您。 |
JSR-166y 库非常完美地为我们解决了 Fork/Join 编排问题,但它还存在一些粗糙的边缘,如果您不够注意,可能会伤害您。您仍然需要处理线程、池和同步屏障。
GPars 抽象便利层
GPars 可以隐藏处理线程、池、屏障和 RecursiveActions 的复杂性,但仍可让您利用 jsr166y 中强大的 Fork/Join 实现。
ForkJoin 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import static groovyx.gpars.GParsPool.runForkJoin
import static groovyx.gpars.GParsPool.withPool
//feel free to experiment with the number of fork/join threads in the pool
withPool(1) {pool ->
println """Number of files: ${
runForkJoin(new File("./src")) {file ->
long count = 0
file.eachFile {
if (it.isDirectory()) {
println "Forking a child task for $it"
forkOffChild(it) //fork a child task
} else {
count++
}
}
return count + (childrenResults.sum(0))
//use results of children tasks to calculate and store own result
}
}"""
}
Fork/Join 节约您的资源
由于内部使用 TaskBarrier 类来同步线程,因此 Fork/Join 操作可以在少量线程上安全运行。
当一个线程在算法内部被阻塞,等待其子任务被计算时,该线程会悄悄地返回到池中,以接管任务队列中任何可用的子任务并处理它们。虽然该算法会创建与子目录一样多的任务,并且任务会等待子目录任务完成,但只要一个线程就足以使计算持续进行,并最终计算出一个有效的结果。
如果您想了解更多信息,请查看 用户指南中的 _Fork/Join 部分.