85 votes

Différence entre le fil et la coroutine chez Kotlin

Est-il un langage spécifique de la mise en œuvre de Kotlin, qui se distingue des autres langues de la mise en œuvre de coroutines?

  • Ce qui signifie que la coroutine est comme la lumière-poids de fil?
  • Quelle est la différence?
  • Sont kotlin coroutines effectivement en cours d'exécution en parallèle / en même temps?
  • Même dans le système multi-core, il y a seulement une coroutine cours d'exécution à un moment donné (est-il juste?)

Ici, je suis de départ de 100000 coroutines, ce qui se passe derrière ce code?

for(i in 0..100000){
   async(CommonPool){
    //run long running operations
  }
}

81voto

Roman Elizarov Points 8871

Ce qui signifie que la coroutine est comme la lumière-poids de fil?

Coroutine, comme un fil, représente une séquence d'actions qui sont exécutées simultanément avec d'autres coroutines (threads).

Quelle est la différence?

Un thread est directement liée à la thread natif dans l'OS (système d'exploitation) et consomme une quantité considérable de ressources. En particulier, il consomme beaucoup de mémoire pour sa pile. C'est pourquoi vous ne pouvez pas créer de 100k threads. Vous êtes susceptible de manquer de mémoire. La commutation entre les threads implique noyau de système d'exploitation répartiteur et c'est une jolie opération coûteuse en termes de cycles de CPU consommé.

Une coroutine, d'autre part, est purement un utilisateur de la langue de l'abstraction. Il n'a pas d'égalité des ressources natives et, dans le cas le plus simple, utilise seulement un nombre relativement petit objet dans le segment de la JVM. C'est pourquoi il est facile de créer de 100k coroutines. La commutation entre les coroutines n'implique pas de noyau de système d'exploitation. Il peut être aussi bon marché que l'invocation d'une fonction régulière.

Sont kotlin de coroutines effectivement en cours d'exécution en parallèle / en même temps? Même dans le système multi-core, il y a seulement une coroutine cours d'exécution à un moment donné (est-il juste?)

Une coroutine peut être en cours d'exécution ou suspendu. Une suspension coroutine est pas associé à un fil, mais l'exécution d'un coroutine s'exécute sur certains thread (à l'aide d'un fil est la seule façon d'exécuter quoi que ce soit à l'intérieur d'un processus du système d'exploitation). Si les différents coroutines fonctionnent toutes sur le même thread a donc ne peut utiliser qu'un seul PROCESSEUR dans un système multicœur) ou dans différents threads (et peuvent donc les utiliser plusieurs Processeurs) est purement dans les mains d'un programmeur qui est à l'aide de coroutines.

Dans Kotlin, l'envoi de coroutines est contrôlé via coroutine contexte. Vous pouvez en savoir plus sur, puis dans le Guide de kotlinx.coroutines

Ici, je suis de départ de 100000 coroutines, ce qui se passe derrière ce code?

En supposant que vous utilisez launch la fonction et de l' CommonPool contexte de l' kotlinx.coroutines projet (qui est open source), vous pouvez examiner le code source ici:

L' launch seulement crée de nouvelles coroutine, tout en CommonPool distribue les coroutines à un ForkJoinPool.commonPool() qui utilise plusieurs threads et donc s'exécute sur plusieurs Processeurs dans cet exemple.

Le code qui suit, launch invocation en {...} est appelé une suspension de lambda. Quelle est-elle et comment la suspension des lambdas et les fonctions mises en œuvre (compilé) ainsi que de la bibliothèque standard de fonctions et de classes comme startCoroutines, suspendCoroutine et CoroutineContext est expliqué à la correspondante de Kotlin coroutines document de conception.

76voto

IRus Points 6031

Depuis que j'ai utilisé coroutines seulement sur la JVM, je vais vous parler de la JVM en arrière-plan, il y a aussi des Kotlin Natif et Kotlin JavaScript mais ces backends pour Kotlin sont hors de ma portée.

Donc, nous allons commencer avec la comparaison de Kotlin coroutines à d'autres langues coroutines. Fondamentalement, vous devez savoir qu'il existe deux types de Coroutines: stackless et stackful. Kotlin implémente stackless coroutines - cela signifie que coroutine ne dispose pas de sa propre pile, et que le fait de limiter un peu ce que coroutine peut faire. Vous pouvez lire une bonne explication ici.

Exemples:

  • Stackless: C#, Scala, Kotlin
  • Stackful: Quasar, Javaflow

Ce que cela signifie que la coroutine est comme la lumière-poids de fil?

Cela signifie que coroutine dans Kotlin ne dispose pas de sa propre pile, il n'a pas de carte sur un thread natif, il ne nécessite pas de changement de contexte sur un processeur.

Quelle est la différence?

Fil - de manière préventive multitâche. (généralement). Coroutine - collaboration multitâche.

Fil - géré par l'OS (généralement). Coroutine - géré par un utilisateur.

Sont kotlin de coroutines effectivement en cours d'exécution en parallèle / en même temps?

Ça dépend, vous pouvez exécuter chaque coroutine dans son propre thread, ou vous pouvez exécuter tous les coroutines dans un thread ou un niveau fixé de pool de threads.

Plus sur la façon de coroutines exécuter ici.

Même dans un système multi-core, il y a seulement une coroutine cours d'exécution à un moment donné (est-il juste?)

Non, voir la réponse précédente.

Ici, je suis de départ de 100000 coroutines, ce qui se passe derrière ce code?

En fait, ça dépend. Mais supposons que vous écrivez le code suivant:

fun main(args: Array<String>) {
    for (i in 0..100000) {
        async(CommonPool) {
            delay(1000)
        }
    }
}

Ce code s'exécute instantanément.

Parce que nous devons attendre les résultats de async appel.

Donc, nous allons résoudre ce problème:

fun main(args: Array<String>) = runBlocking {
    for (i in 0..100000) {
        val job = async(CommonPool) {
            delay(1)
            println(i)
        }

        job.join()
    }
}

Lorsque vous exécutez ce programme kotlin permettra de créer 2 * 100000 instances de Continuation, ce qui va prendre quelques dizaines de Mo de RAM, et dans la console, vous verrez des nombres de 1 à 100000.

Donc permet de réécrire ce code de cette façon:

fun main(args: Array<String>) = runBlocking {

    val job = async(CommonPool) {
        for (i in 0..100000) {
            delay(1)
            println(i)
        }
    }

    job.join()
}

Ce que nous réalisons maintenant? Maintenant nous allons créer seulement 100001 cas d' Continuation, et c'est beaucoup mieux.

Chaque suite sera distribué et exécuté sur CommonPool (qui est une instance statique de ForkJoinPool).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X