Kotlin Process Collection并行?

时间:2017-08-08 18:25:26

标签: collections parallel-processing kotlin kotlinx.coroutines

我有一组对象,我需要对它进行一些转换。目前我正在使用:

var myObjects: List<MyObject> = getMyObjects()

myObjects.forEach{ myObj ->
    someMethod(myObj)
}

它工作正常,但我希望通过并行运行someMethod()来加速它,而不是等待每个对象完成,然后再开始下一个。

在Kotlin有什么办法吗?也许用doAsyncTask或其他东西?

我知道这是asked over a year ago这是不可能的,但现在Kotlin有像doAsyncTask这样的协程我很好奇,如果任何协同程序可以提供帮助

3 个答案:

答案 0 :(得分:8)

是的,这可以使用协程来完成。以下函数对集合的所有元素并行应用操作:

fun <A>Collection<A>.forEachParallel(f: suspend (A) -> Unit): Unit = runBlocking {
    map { async(CommonPool) { f(it) } }.forEach { it.await() }
}

虽然定义本身有点神秘,但您可以轻松地按照您的预期应用它:

myObjects.forEachParallel { myObj ->
    someMethod(myObj)
}

可以用类似的方式实现并行映射,请参阅https://stackoverflow.com/a/45794062/1104870

答案 1 :(得分:4)

Java Stream在Kotlin中很容易使用:

tasks.stream().parallel().forEach { computeNotSuspend(it) }

如果您使用的是Android,则如果您希望应用程序与低于24的API兼容,则无法使用Java 8.

您也可以按照建议使用协同程序。但它现在(2017年8月)并不是语言的一部分,您需要安装外部库。非常好guide with examples

    runBlocking<Unit> {
        val deferreds = tasks.map { async(CommonPool) { compute(it) } }
        deferreds.forEach { it.await() }
    }

请注意,协同程序使用非阻塞多线程实现,这意味着它们可以比传统的多线程更快。我有以下代码对Stream并行与协同程序进行基准测试,在这种情况下,协程方法在我的机器上快7倍。 但是你必须自己做一些工作以确保你的代码是“暂停”(非锁定),这可能非常棘手。在我的例子中,我只是调用delay这是库提供的suspend功能。非阻塞多线程并不总是比传统的多线程更快。如果你有许多线程只是在等待IO,这可能会更快,这正是我的基准测试所做的。

我的基准代码:

import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.runBlocking
import java.util.*
import kotlin.system.measureNanoTime
import kotlin.system.measureTimeMillis

class SomeTask() {
    val durationMS = random.nextInt(1000).toLong()

    companion object {
        val random = Random()
    }
}

suspend fun compute(task: SomeTask): Unit {
    delay(task.durationMS)
    //println("done ${task.durationMS}")
    return
}

fun computeNotSuspend(task: SomeTask): Unit {
    Thread.sleep(task.durationMS)
    //println("done ${task.durationMS}")
    return
}

fun main(args: Array<String>) {
    val n = 100
    val tasks = List(n) { SomeTask() }

    val timeCoroutine = measureNanoTime {
        runBlocking<Unit> {
            val deferreds = tasks.map { async(CommonPool) { compute(it) } }
            deferreds.forEach { it.await() }
        }
    }

    println("Coroutine ${timeCoroutine / 1_000_000} ms")

    val timePar = measureNanoTime {
        tasks.stream().parallel().forEach { computeNotSuspend(it) }
    }
    println("Stream parallel ${timePar / 1_000_000} ms")
}

我的4核计算机上的输出:

Coroutine: 1037 ms
Stream parallel: 7150 ms

如果您取消注释两个println函数中的compute,您会看到在非阻塞协程代码中,任务按正确的顺序处理,但不会与Streams一起处理。

答案 2 :(得分:0)

您可以使用RxJava来解决此问题。

List<MyObjects> items = getList()

Observable.from(items).flatMap(object : Func1<MyObjects, Observable<String>>() {
    fun call(item: MyObjects): Observable<String> {
        return someMethod(item)
    }
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : Subscriber<String>() {
    fun onCompleted() {

    }

    fun onError(e: Throwable) {

    }

    fun onNext(s: String) {
        // do on output of each string
    }
})

通过订阅Schedulers.io(),可以在后台线程上安排一些方法。