要以 PDF 格式阅读本主题,请点击此处

GParsPool 并发集合处理

处理数据通常涉及操作集合。列表、数组、集合、映射、迭代器、字符串和许多其他数据类型都可以被视为项目的集合。处理此类集合的常见模式是按顺序逐个获取元素,并对每个元素进行操作。

例如,min() 函数应该返回集合中最小的元素。当您在数字集合上调用 min() 方法时,调用线程将创建一个 累加器迄今为止最小的值,并将其初始化为给定类型的最小值,例如零。然后,线程将遍历集合中的元素,并将它们与 累加器 中的值进行比较。

处理完所有元素后,最小值将存储在 累加器 中。

这种算法虽然简单,但在多核硬件上是完全错误的

在双核芯片上运行 min() 函数最多只能利用芯片50%的计算能力。在四核芯片上,它将只有 25%。


这种算法实际上浪费了 75% 的计算能力

正确!这种算法实际上浪费了 75% 的计算能力

树状结构被证明更适合并行处理。我们示例中的 min() 函数不需要按顺序遍历所有元素,并将它们的值与 累加器 进行比较。相反,它可以利用硬件的多核特性。

例如,parallel_min() 函数可以比较集合中相邻值的对(或特定大小的元组),并将元组中最小的值提升到下一轮比较中。

在不同的元组中搜索最小值可以安全地并行进行,因此同一轮中的元组可以由不同的内核同时处理,而不会出现线程之间的竞争或争用。

GParsPool 类在集合上启用了基于ParallelArray(来自 JSR-166y)的 DSL。

使用示例

带有 Pool 的 GParsPool 示例
1
2
3
4
5
6
7
8
9
10
11
GParsPool.withPool {
    def selfPortraits = images.findAllParallel{
        it.contains me}.collectParallel {it.resize()
    }

    //a map-reduce functional style
    def smallestSelfPortrait = images.parallel
        .filter{it.contains me}
        .map{it.resize()}
        .min{it.sizeInMB}
}

GParsExecutorsPool 类提供了与 GParsPool 类类似的功能,但使用 JDK 线程池而不是更高效的基于ParallelArray(来自JSR-166y)的实现。

使用示例

GParsExecutorsPool.withPool
1
2
3
4
5
GParsExecutorsPool.withPool {
    def selfPortraits = images
        .findAllParallel{it.contains me}
        .collectParallel {it.resize()}
}

有关更多信息,请参阅用户指南中的并行集合部分