Gibt es eine spezifische Sprachimplementierung in Kotlin, die sich von der Implementierung von Coroutinen in einer anderen Sprache unterscheidet?
- Was bedeutet, dass Coroutine wie ein leichter Faden ist?
- Was ist der Unterschied?
- Laufen Kotlin-Coroutinen tatsächlich parallel / gleichzeitig?
- Selbst in Mehrkernsystemen läuft immer nur eine Coroutine (stimmt das?)
Hier starte ich 100000 Coroutinen. Was passiert hinter diesem Code?
for(i in 0..100000){
async(CommonPool){
//run long running operations
}
}
kotlin
kotlin-coroutines
Jemo Mgebrishvili
quelle
quelle
Antworten:
Da ich Coroutinen nur für JVM verwendet habe, werde ich über das JVM-Backend sprechen. Es gibt auch Kotlin Native und Kotlin JavaScript, aber diese Backends für Kotlin liegen außerhalb meines Anwendungsbereichs.
Beginnen wir also mit dem Vergleich von Kotlin-Coroutinen mit Coroutinen anderer Sprachen. Grundsätzlich sollten Sie wissen, dass es zwei Arten von Coroutinen gibt: stapellos und stapelbar. Kotlin implementiert stapellose Coroutinen - dies bedeutet, dass Coroutine keinen eigenen Stapel hat und die Möglichkeiten von Coroutine ein wenig einschränken. Eine gute Erklärung können Sie hier lesen .
Beispiele:
Dies bedeutet, dass Coroutine in Kotlin keinen eigenen Stack hat, nicht auf einem nativen Thread abgebildet wird und keine Kontextumschaltung auf einem Prozessor erforderlich ist.
Thread - präventiv Multitasking. ( normalerweise ). Coroutine - kooperatives Multitasking.
Thread - wird normalerweise vom Betriebssystem verwaltet. Coroutine - von einem Benutzer verwaltet.
Es hängt davon ab, ob Sie jede Coroutine in einem eigenen Thread ausführen oder alle Coroutinen in einem Thread oder einem festen Thread-Pool ausführen können.
Mehr darüber, wie Coroutinen ausgeführt werden, finden Sie hier .
Nein, siehe vorherige Antwort.
Eigentlich kommt es darauf an. Angenommen, Sie schreiben den folgenden Code:
fun main(args: Array<String>) { for (i in 0..100000) { async(CommonPool) { delay(1000) } } }
Dieser Code wird sofort ausgeführt.
Weil wir auf die Ergebnisse des
async
Anrufs warten müssen .Lassen Sie uns das beheben:
fun main(args: Array<String>) = runBlocking { for (i in 0..100000) { val job = async(CommonPool) { delay(1) println(i) } job.join() } }
Wenn Sie dieses Programm ausführen, erstellt kotlin 2 * 100000 Instanzen von
Continuation
, was einige Dutzend MB RAM beansprucht , und in der Konsole werden Zahlen von 1 bis 100000 angezeigt.Schreiben wir diesen Code also folgendermaßen um:
fun main(args: Array<String>) = runBlocking { val job = async(CommonPool) { for (i in 0..100000) { delay(1) println(i) } } job.join() }
Was erreichen wir jetzt? Jetzt erstellen wir nur 100001 Instanzen von
Continuation
, und das ist viel besser.Jede erstellte Fortsetzung wird auf CommonPool (einer statischen Instanz von ForkJoinPool) gesendet und ausgeführt.
quelle
throws SuspendExecution
demsuspend
Modifikator von Quasar und Kotlin zu erkennen . Die Implementierungsdetails sind natürlich sehr unterschiedlich, aber die Benutzererfahrung ist ziemlich ähnlich.Coroutine repräsentiert wie ein Thread eine Folge von Aktionen, die gleichzeitig mit anderen Coroutinen (Threads) ausgeführt werden.
Ein Thread ist direkt mit dem nativen Thread im entsprechenden Betriebssystem (Betriebssystem) verbunden und verbraucht eine beträchtliche Menge an Ressourcen. Insbesondere verbraucht es viel Speicher für seinen Stapel. Deshalb können Sie nicht einfach 100.000 Threads erstellen. Es ist wahrscheinlich, dass Ihnen der Speicher ausgeht. Das Wechseln zwischen Threads erfordert einen OS-Kernel-Dispatcher und ist in Bezug auf die verbrauchten CPU-Zyklen ein ziemlich teurer Vorgang.
Eine Coroutine hingegen ist eine reine Sprachabstraktion auf Benutzerebene. Es bindet keine nativen Ressourcen und verwendet im einfachsten Fall nur ein relativ kleines Objekt im JVM-Heap. Aus diesem Grund ist es einfach, 100.000 Coroutinen zu erstellen. Das Wechseln zwischen Coroutinen beinhaltet überhaupt keinen Betriebssystemkern. Es kann so billig sein wie das Aufrufen einer regulären Funktion.
Eine Coroutine kann entweder ausgeführt oder ausgesetzt werden. Eine angehaltene Coroutine ist keinem bestimmten Thread zugeordnet, aber eine laufende Coroutine wird auf einem Thread ausgeführt (die Verwendung eines Threads ist die einzige Möglichkeit, etwas innerhalb eines Betriebssystemprozesses auszuführen). Ob verschiedene Coroutinen alle auf demselben Thread ausgeführt werden (a verwendet möglicherweise nur eine einzige CPU in einem Multicore-System) oder in unterschiedlichen Threads (und somit möglicherweise mehrere CPUs), liegt ausschließlich in den Händen eines Programmierers, der Coroutinen verwendet.
In Kotlin wird der Versand von Coroutinen über den Coroutine-Kontext gesteuert . Weitere Informationen finden Sie im Handbuch zu kotlinx.coroutines
Angenommen, Sie verwenden
launch
Funktion undCommonPool
Kontext aus demkotlinx.coroutines
Projekt (Open Source), können Sie den Quellcode hier untersuchen:launch
wird hier definiert https://github.com/Kotlin/kotlinx.coroutines/blob/master/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.ktCommonPool
wird hier definiert https://github.com/Kotlin/kotlinx.coroutines/blob/master/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CommonPool.ktDas
launch
erstellt nur eine neue Coroutine, währendCommonPool
Coroutinen an eine gesendet werden,ForkJoinPool.commonPool()
die mehrere Threads verwendet und daher in diesem Beispiel auf mehreren CPUs ausgeführt wird.Der Code, der auf den
launch
Aufruf von folgt,{...}
wird als suspendierendes Lambda bezeichnet . Was ist das und wie umgesetzt wird gesperrt , lambdas und Funktionen (kompilierten) sowie Standard - Library - Funktionen und Klassen mögenstartCoroutines
,suspendCoroutine
undCoroutineContext
in dem entsprechenden erklärt Kotlin Koroutinen Designdokument .quelle
Executor
es eine Beziehung zwischen diesen beiden?