Parallel processing Kotlin集合上的并行操作?
在Scala中,您可以使用以下工具轻松地执行并行映射、forEach等操作:Parallel processing Kotlin集合上的并行操作?,parallel-processing,kotlin,Parallel Processing,Kotlin,在Scala中,您可以使用以下工具轻松地执行并行映射、forEach等操作: collection.par.map(..) Kotlin是否有一个等价物?目前没有。与Scala相比,官方Kotlin提到: 以后可能添加到Kotlin的内容: 平行集合 Kotlin标准库不支持并行操作。但是,由于Kotlin使用标准Java集合类,因此也可以使用Java8流API对Kotlin集合执行并行操作 e、 g。 Kotlin的stdlib中还没有官方支持,但是您可以定义一个模拟par.map:
collection.par.map(..)
Kotlin是否有一个等价物?目前没有。与Scala相比,官方Kotlin提到: 以后可能添加到Kotlin的内容:
- 平行集合
Kotlin标准库不支持并行操作。但是,由于Kotlin使用标准Java集合类,因此也可以使用Java8流API对Kotlin集合执行并行操作 e、 g。
Kotlin的stdlib中还没有官方支持,但是您可以定义一个模拟
par.map
:
fun <T, R> Iterable<T>.pmap(
numThreads: Int = Runtime.getRuntime().availableProcessors() - 2,
exec: ExecutorService = Executors.newFixedThreadPool(numThreads),
transform: (T) -> R): List<R> {
// default size is just an inlined version of kotlin.collections.collectionSizeOrDefault
val defaultSize = if (this is Collection<*>) this.size else 10
val destination = Collections.synchronizedList(ArrayList<R>(defaultSize))
for (item in this) {
exec.submit { destination.add(transform(item)) }
}
exec.shutdown()
exec.awaitTermination(1, TimeUnit.DAYS)
return ArrayList<R>(destination)
}
如果需要,它允许通过提供线程数甚至特定的java.util.concurrent.Executor
来调整线程。例如
listOf("foo", "bar").pmap(4, transform = { it + "!" })
请注意,这种方法只允许并行化
map
操作,不影响任何下游位。例如,第一个示例中的过滤器将运行单线程。然而,在许多情况下,仅数据转换(即map
)需要并行化。此外,将该方法从上面扩展到Kotlin collection API的其他元素也很简单。从Kotlin 1.1开始,并行操作也可以非常优雅地表示为。以下是列表中的pmap
:
fun <A, B>List<A>.pmap(f: suspend (A) -> B): List<B> = runBlocking {
map { async(CommonPool) { f(it) } }.map { it.await() }
}
fun List.pmap(f:suspend(A)->B):List=runBlocking{
map{async(CommonPool){f(it)}}.map{it.await()}
}
请注意,协同程序仍然是一个实验性的特性。从1.2版本开始,kotlin添加了一个与JRE8兼容的
因此,异步迭代列表可以如下所示:
fun main(args: Array<String>) {
val c = listOf("toto", "tata", "tutu")
c.parallelStream().forEach { println(it) }
}
fun main(args:Array){
val c=列表(“托托”、“塔塔”、“图图”)
c、 parallelStream().forEach{println(it)}
}
Kotlin希望自己的语言习惯化,但不要太过合成,这样乍一看就很难理解
通过协同程序进行并行计算也不例外。他们希望它是简单的,但不是隐式的,使用一些预先构建的方法,允许在需要时进行分支计算
就你而言:
collection.map {
async{ produceWith(it) }
}
.forEach {
consume(it.await())
}
请注意,要调用async
和await
您需要位于所谓的上下文中,您不能从非协同路由上下文中挂起调用或启动协同路由。要输入一个,您可以:
runBlocking{/*此处的代码*/}
:它将挂起当前线程,直到lambda返回
GlobalScope.launch{}
:它将并行执行lambda;如果您的main
完成了执行,而您的协同程序没有发生错误,那么最好使用runBlocking
希望有帮助:)此解决方案假定您的项目正在使用协同程序:
implementation( "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2")
名为parallelTransform
的函数不保留元素的顺序并返回流
,而parallelMap
函数保留顺序并返回列表
为多个调用创建线程池:
val numberOfCores = Runtime.getRuntime().availableProcessors()
val executorDispatcher: ExecutorCoroutineDispatcher =
Executors.newFixedThreadPool(numberOfCores ).asCoroutineDispatcher()
使用该调度器(当不再需要时调用close()
):
inline fun Iterable.parallelTransform(
调度员:执行调度员,
交叉内联变换:(T)->R
):流量=渠道流量{
val项目:Iterable=this@parallelTransform
val channelFlowScope:ProducerScope=this@channelFlow
发射(调度员){
items.forEach{item->
发射{
channelFlowScope.send(转换(项目))
}
}
}
}
如果不考虑线程池重用(线程池并不便宜),则可以使用以下版本:
inline fun <T, R> Iterable<T>.parallelTransform(
numberOfThreads: Int,
crossinline transform: (T) -> R
): Flow<R> = channelFlow {
val items: Iterable<T> = this@parallelTransform
val channelFlowScope: ProducerScope<R> = this@channelFlow
Executors.newFixedThreadPool(numberOfThreads).asCoroutineDispatcher().use { dispatcher ->
launch( dispatcher ) {
items.forEach { item ->
launch {
channelFlowScope.send(transform(item))
}
}
}
}
}
inline fun Iterable.parallelTransform(
numberOfThreads:Int,
交叉内联变换:(T)->R
):流量=渠道流量{
val项目:Iterable=this@parallelTransform
val channelFlowScope:ProducerScope=this@channelFlow
Executors.newFixedThreadPool(numberOfThreads).asCoroutineDispatcher()。使用{dispatcher->
发射(调度员){
items.forEach{item->
发射{
channelFlowScope.send(转换(项目))
}
}
}
}
}
如果需要保留元素顺序的版本:
inline fun <T, R> Iterable<T>.parallelMap(
dispatcher: ExecutorDispatcher,
crossinline transform: (T) -> R
): List<R> = runBlocking {
val items: Iterable<T> = this@parallelMap
val result = ConcurrentSkipListMap<Int, R>()
launch(dispatcher) {
items.withIndex().forEach {(index, item) ->
launch {
result[index] = transform(item)
}
}
}
// ConcurrentSkipListMap is a SortedMap
// so the values will be in the right order
result.values.toList()
}
inline fun Iterable.parallelMap(
调度员:执行调度员,
交叉内联变换:(T)->R
):List=runBlocking{
val项目:Iterable=this@parallelMap
val结果=ConcurrentSkipListMap()
发射(调度员){
items.withIndex().forEach{(索引,项)->
发射{
结果[索引]=转换(项目)
}
}
}
//ConcurrentSkipListMap是一个分类地图
//因此,这些值的顺序是正确的
result.values.toList()
}
您可以使用此扩展方法:
suspend fun <A, B> Iterable<A>.pmap(f: suspend (A) -> B): List<B> = coroutineScope {
map { async { f(it) } }.awaitAll()
}
suspend fun以获取更多信息我发现另一种非常优雅的方法是这样的,使用库:
导入kotlinx.coroutines.flow.asFlow
暂停乐趣流程(myCollection:Iterable){
myCollection.asFlow()
.map{/*…*/}
.filter{/*…*/}
.collect{/*…执行一些副作用…*/}
}
然而,它确实需要额外的依赖性kotlinx.coroutines
不在stdlib中。一些最快的并行集合来自GS集合:。。。您可以从Kotlin使用它(正如任何Java收集框架都可以使用一样)。如何在Kotlin中使用Java8流API?@LordScone,方法与在Java中使用的方法相同。例如:myCollection.parallelStream().map{…}。filter{…}
仅供参考,parallelStream仅在长列表的情况下建议使用,对于较小的列表,在java world中的不同线程中运行这两个作业将是一种开销。尽可能地坚持合作。我看不出“destination.ad
inline fun <T, R> Iterable<T>.parallelTransform(
numberOfThreads: Int,
crossinline transform: (T) -> R
): Flow<R> = channelFlow {
val items: Iterable<T> = this@parallelTransform
val channelFlowScope: ProducerScope<R> = this@channelFlow
Executors.newFixedThreadPool(numberOfThreads).asCoroutineDispatcher().use { dispatcher ->
launch( dispatcher ) {
items.forEach { item ->
launch {
channelFlowScope.send(transform(item))
}
}
}
}
}
inline fun <T, R> Iterable<T>.parallelMap(
dispatcher: ExecutorDispatcher,
crossinline transform: (T) -> R
): List<R> = runBlocking {
val items: Iterable<T> = this@parallelMap
val result = ConcurrentSkipListMap<Int, R>()
launch(dispatcher) {
items.withIndex().forEach {(index, item) ->
launch {
result[index] = transform(item)
}
}
}
// ConcurrentSkipListMap is a SortedMap
// so the values will be in the right order
result.values.toList()
}
suspend fun <A, B> Iterable<A>.pmap(f: suspend (A) -> B): List<B> = coroutineScope {
map { async { f(it) } }.awaitAll()
}