web-dev-qa-db-fra.com

Différence entre le fil et la coroutine chez Kotlin

Existe-t-il une implémentation linguistique spécifique dans Kotlin, qui la diffère de l'implémentation de coroutines dans une autre langue?

  • Qu'est-ce qui signifie que la coroutine est comme un fil léger?
  • Quelle est la différence?
  • Les routines Kotlin fonctionnent-elles en parallèle/simultanément?
  • Même dans les systèmes multicœurs, il n’existe qu’une seule coroutine à la fois (est-ce exact?)

Ici, je lance 100 000 coroutines, que se passe-t-il derrière ce code?

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

Puisque j’ai utilisé des coroutines uniquement sur JVM, je parlerai du backend JVM, il existe également Kotlin Native et Kotlin JavaScript, mais ces backends pour Kotlin sont hors de ma portée.

Commençons donc par comparer les coroutines Kotlin aux coroutines d’autres langues. En gros, vous devez savoir qu'il existe deux types de Coroutines: empilable et empilé. Kotlin implémente des routines sans pile - cela signifie que la coroutine ne possède pas sa propre pile et limite un peu ce que coroutine peut faire. Vous pouvez lire une bonne explication ici .

Exemples:

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

Qu'est-ce que cela signifie que la coroutine est comme un fil léger?

Cela signifie que la coroutine de Kotlin ne possède pas sa propre pile, ne mappe pas sur un thread natif, elle n'exige pas de changement de contexte sur un processeur.

Quelle est la différence?

Fil - multitâche préemptif. ( généralement ). Coroutine - multitâche en coopération.

Thread - géré par le système d'exploitation (généralement). Coroutine - géré par un utilisateur.

Les coroutines de Kotlin fonctionnent-elles réellement en parallèle/simultanément?

Cela dépend, vous pouvez exécuter chaque coroutine dans son propre thread ou toutes les coroutines dans un thread ou dans un pool de threads fixe.

En savoir plus sur l'exécution des coroutines ici .

Même dans un système multicœur, il n’existe qu’une seule coroutine à la fois (est-ce exact?)

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

Ici, je lance 100 000 coroutines, que se passe-t-il derrière ce code?

En fait, ça dépend. Mais supposons que vous écriviez 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 l'appel async.

Alors corrigeons ceci:

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

        job.join()
    }
}

Lorsque vous exécuterez ce programme, kotlin créera 2 * 100000 instances de Continuation, qui prendront quelques dizaines de Mo de RAM, et dans la console, vous verrez des nombres compris entre 1 et 100000.

Donc, réécrivons ce code de la manière suivante:

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

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

    job.join()
}

Qu'est-ce que nous réalisons maintenant? Maintenant, nous créons seulement 100001 instances de Continuation, et c'est beaucoup mieux.

Chaque continuation créée sera envoyée et exécutée sur CommonPool (qui est une instance statique de ForkJoinPool).

44
Ruslan

Qu'est-ce qui signifie que la coroutine est comme un fil léger?

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

Quelle est la différence?

Un thread est directement lié au thread natif du système d'exploitation correspondant (système d'exploitation) et consomme une quantité considérable de ressources. En particulier, il utilise beaucoup de mémoire pour sa pile. C'est pourquoi vous ne pouvez pas simplement créer 100k threads. Vous êtes susceptible de manquer de mémoire. La commutation entre les threads implique le répartiteur du noyau du système d'exploitation, ce qui représente une opération assez coûteuse en termes de cycles de processeur consommés.

Une coroutine, par contre, est purement une abstraction de langage au niveau utilisateur. Il ne lie aucune ressource native et, dans le cas le plus simple, n'utilise qu'un seul objet relativement petit dans le segment de mémoire de la JVM. C'est pourquoi il est facile de créer 100K coroutines. Changer de coroutine n'implique pas du tout le noyau du système d'exploitation. Cela peut être aussi bon marché que d'invoquer une fonction régulière.

Est-ce que les coroutines de Kotlin fonctionnent en parallèle/simultanément? Même dans les systèmes multicœurs, il n’existe qu’une seule coroutine à la fois (est-ce exact?)

Une coroutine peut être en cours d'exécution ou suspendue. Une coroutine suspendue n'est pas associée à un thread particulier, mais une coroutine en cours d'exécution s'exécute sur un thread (l'utilisation d'un thread est le seul moyen d'exécuter quoi que ce soit dans un processus de système d'exploitation). Que différents coroutines fonctionnent tous sur le même thread (un peut donc n'utiliser qu'un seul processeur dans un système multicœur) ou dans des threads différents (et peuvent donc utiliser plusieurs processeurs), cela relève uniquement du programmeur qui utilise des coroutines.

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

Ici, je lance 100 000 coroutines, que se passe-t-il derrière ce code?

En supposant que vous utilisez launch fonction et CommonPool contexte du projet kotlinx.coroutines (Qui est open source), vous pouvez examiner leur code source ici:

launch crée simplement une nouvelle coroutine, tandis que CommonPool distribue des coroutines dans un ForkJoinPool.commonPool() qui utilise plusieurs threads et s'exécute donc sur plusieurs processeurs dans cet exemple.

Le code qui suit launch l'invocation dans {...} Est appelé un lambda suspendant . Qu'est-ce que c'est et comment suspendre les lambdas et les fonctions implémentées (compilées), ainsi que les fonctions et les classes de bibliothèque standard comme startCoroutines, suspendCoroutine et CoroutineContext est expliqué dans la section correspondante - document de conception des coroutines Kotlin .

51
Roman Elizarov