Ich schreibe eine Backend-Anwendung in Kotlin.
Um die Arbeit zu beschleunigen, verlasse ich mich derzeit auf RxKotlin auf dem Server, um E / A-Aufgaben wie Datenbankaufrufe und API-Aufrufe parallel auszuführen. Der Code sieht normalerweise so aus.
val singleResult1 = Single.fromCallable{
database.get(....)
}.io()
val singleResult2 = Single.fromCallable{
database.update(....)
}.io()
Single.zip(singleResult1, singleResult2){ result1: Result1, result2: Result2 ->
....
}
.flatMap{
//other RX calls
}
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.blockingGet()
Da jedoch nicht wirklich mit mehreren Ereignissen (nur Singles) funktioniert, fühlt sich Rx etwas chaotisch an und fügt nur ein paar Boilerplates hinzu (es verursacht auch Komplikationen, wenn ich einen Nullwert zurückgeben möchte und manchmal den Stack-Trace durcheinander bringen könnte )
Ich denke darüber nach, Rx zu entfernen und Executors
stattdessen (oder Threads) für Parallelität zu verwenden. Gibt es hier Leistungsüberlegungen zu berücksichtigen?
Beispiel, woran ich denke:
fun <T> waitAll(tasks: List<Callable<T>>, threadCount: Int = -1): List<T> {
val threads = if (threadCount == -1) tasks.size else threadCount
val executor = Executors.newFixedThreadPool(threads)
val results = executor.invokeAll(tasks).map {
it.get()
}
executor.shutdown()
return results
}
Und wie folgt:
waitAll(listOf(callable1, callable2))
Oder vielleicht normale Threads verwenden und sie verbinden?
threads.forEach{
it.start()
}
threads.forEach{
it.join()
}
Oder warum nicht Streams?
listOf(callable1,callable2)
.parallelStream()
.map{it.call()}
.collect(Collectors.toList())
quelle
Antworten:
KotlinRx selbst verwendet Executoren, außer dass es zwei vorgefertigte Thread-Pools unter
Schedulers.io()
(unbegrenzt) undSchedulers.computation()
(begrenzt durch die Anzahl der Kerne) hat und nicht jedes Mal einen neuen hochfährt, wie es Ihr vorgeschlagener Code tut. Was Sie natürlich auch manuell tun können:Dies sollte im Allgemeinen besser sein als das Erstellen eines Threads für jede Aufgabe, indem vorhandene Threads wiederverwendet werden können, hängt jedoch von Ihrer Verwendung ab.
Ob Coroutinen dafür sehr nützlich sind, hängt davon ab, was Sie in Ihren Aufgaben haben. Eine blockierende API (zB JDBC)? Sie sind nicht. Eine asynchrone (zB Nachrüstung)? Sie sind.
quelle
Java Executor-Dienste verwenden Threads und RxKotlin verwendet ExecutorServices. Alle diese sind also im Hintergrund gleich. Der Unterschied liegt in der Softwarearchitektur. Wenn Sie also die beste Architektur auswählen, die in Ihren Code integriert werden soll, funktioniert sie am besten und erledigt die Aufgabe korrekt. Einfach ist das Beste.
Wenn Sie eine ereignisbasierte oder beobachtbare Architektur haben und versuchen, eine neue Bibliothek für die Arbeit mit ereignisbasierten Vorgängen oder Jobs zu implementieren, können Sie falsche Schritte zur Jobtrennung oder zum Timing schreiben. Verwenden Sie RxKotlin und erfinden Sie das Rad nicht erneut.
Wenn es bei Ihrer Arbeit nicht um Ereignisse oder beobachtbare Muster geht und Sie nur parallele Jobs ausführen müssen, verwenden Sie einfach die Executor-Dienste. Der RxKotlin wird überarbeitet. Wenn Sie RxKotlin in dieser Situation verwenden, müssen Sie mehr tun, als Sie benötigen.
Ich denke, die Frage ist nicht die Geschwindigkeit in dieser Situation, sondern die Architektur.
quelle