我有一组对象,我需要对它进行一些转换。目前我正在使用:
var myObjects: List<MyObject> = getMyObjects()
myObjects.forEach{ myObj ->
someMethod(myObj)
}
它工作正常,但我希望通过并行运行someMethod()
来加速它,而不是等待每个对象完成,然后再开始下一个。
在Kotlin有什么办法吗?也许用doAsyncTask
或其他东西?
我知道这是asked over a year ago这是不可能的,但现在Kotlin有像doAsyncTask
这样的协程我很好奇,如果任何协同程序可以提供帮助
答案 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()
,可以在后台线程上安排一些方法。