Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Kotlin 从多个来源收集数据的惯用方法是什么?_Kotlin_Kotlin Coroutines - Fatal编程技术网

Kotlin 从多个来源收集数据的惯用方法是什么?

Kotlin 从多个来源收集数据的惯用方法是什么?,kotlin,kotlin-coroutines,Kotlin,Kotlin Coroutines,设想一个数据服务器,数据在40个节点之间随机分片,您希望从中计算每200条记录的值。因此,加载200、计算、加载200、计算等。您的服务器每秒可以处理500条记录,但您有足够的带宽每秒从每台服务器读取50条记录(最大吞吐量为2000条记录) 您可以按顺序执行此操作,这是最简单的选项: var cache = mutableListOf() for (serv in servers) { for(record in serv.loadData()) { cache += r

设想一个数据服务器,数据在40个节点之间随机分片,您希望从中计算每200条记录的值。因此,加载200、计算、加载200、计算等。您的服务器每秒可以处理500条记录,但您有足够的带宽每秒从每台服务器读取50条记录(最大吞吐量为2000条记录)

您可以按顺序执行此操作,这是最简单的选项:

var cache = mutableListOf()
for (serv in servers) {
    for(record in serv.loadData()) {
        cache += record
        if (cache.count() == 500) {
            process(cache)
            cache.popFront(500)
        }

    }
}
这不会浪费内存中的任何空间,但每秒只加载50条记录,并且不会并行处理结果。另一种方法是首先从所有服务器获取结果,然后迭代:

var queue = ConcurrentLinkedDeque()
coroutineScope {
    for (serv in servers) {
        launch(Dispatchers.IO) {
            for (record in serv.loadData()) {
                queue += record
            }
        }
    }
}

for (batch in queue.chunked(500)) {
    process(batch)
}
这将最大限度地利用吞吐量,但会浪费并发队列中的空间,而且as也不允许并行处理和加载

因此,这似乎是一个利用
流的好机会。我们希望保持从多个源并行加载的能力,因此我们将用
emit(record)
替换
queue+=record
,然后在
collect{}
中批处理结果,但是
Flow.emit
不是多线程安全的(由于
启动
,上下文会发生变化,但这是可以克服的,即使这是不可取的)

假设
serv.loadData()
以增量方式加载数据,这仍然可以通过在队列太满时暂停数据加载来实现。但是这样编写会让人感觉非常手动和笨拙


那么-假设您不关心数据的加载顺序-在当前版本的Kotlin中实现这一点的惯用方法是什么?

下面是一种使用
flatMapMerge
的方法,它自动并行您发出的内部流:

suspend fun main() {
    servers.asFlow()
            .flatMapMerge(servers.size) { server -> flow {
                for (record in server.loadData()) {
                    emit(record)
                }
            } }
            .chunked(500)
            .flowOn(Dispatchers.IO) // optional
            .collect { batch ->
                process(batch)
            }
}

fun <T> Flow<T>.chunked(size: Int) = flow {
    var chunk = mutableListOf<T>()
    collect {
        chunk.add(it)
        if (chunk.size == size) {
            emit(chunk)
            chunk = mutableListOf()
        }
    }
    chunk.takeIf { it.isNotEmpty() }?.also { emit(it) }
}
suspend fun main(){
servers.asFlow()
.flatMapMerge(servers.size){server->flow{
for(记录在server.loadData()中){
发射(记录)
}
} }
.分块(500)
.flow on(Dispatchers.IO)//可选
.收集{批处理->
过程(批次)
}
}
乐趣流。分块(大小:Int)=流{
var chunk=mutableListOf()
收集{
chunk.add(它)
if(chunk.size==大小){
发射(块)
chunk=mutableListOf()
}
}
chunk.takeIf{it.isNotEmpty()}?.还有{emit(it)}
}

Flow仍然没有一个标准的
chunked实现,所以我提供了一个快速而肮脏的实现。

很好;我想我不是第一个遇到这种情况的人:)我的想法是,这也需要一个调度器吗?从外部看,我们知道
loadData()
是一个IO绑定的操作,但我们似乎没有办法指定使用
Dispatchers.IO
。或者此并发收集是以其他方式完成的?这取决于
loadData()
是阻塞函数还是挂起函数。我写这篇文章时假设
suspend-fun
。您可以使用上下文(IO)
将整个流程包装成
,也可以在
flatMapMerge
下面添加
.flowOn(IO)