Qu'est-ce qu'une coroutine? Comment sont-ils liés à la concurrence?
Qu'est-ce qu'une coroutine? Comment sont-ils liés à la concurrence?
Réponses:
Les coroutines et la concurrence sont largement orthogonales. Les coroutines sont une structure de contrôle générale par laquelle le contrôle de flux est transmis en coopération entre deux routines différentes sans retour.
L'instruction 'yield' en Python est un bon exemple. Cela crée une coroutine. Lorsque le «rendement» est rencontré, l'état actuel de la fonction est enregistré et le contrôle est renvoyé à la fonction appelante. La fonction appelante peut alors retransférer l'exécution à la fonction de rendement et son état sera restauré au point où le «rendement» a été rencontré et l'exécution se poursuivra.
Coroutines are a general control structure whereby flow control is cooperatively passed between two different routines without returning.
<- C'est la concurrence. Le mot que vous recherchez est le parallélisme.
orthogonal = Not similar to each other
?
orthogonal
signifie "indépendant les uns des autres".
De la programmation dans Lua , Coroutines
section " ":
Une coroutine est similaire à un thread (dans le sens du multithreading): c'est une ligne d'exécution, avec sa propre pile, ses propres variables locales et son propre pointeur d'instruction; mais il partage des variables globales et surtout n'importe quoi d'autre avec d'autres coroutines. La principale différence entre les threads et les coroutines est que, conceptuellement (ou littéralement, dans une machine multiprocesseur), un programme avec des threads exécute plusieurs threads en parallèle. Les coroutines, en revanche, sont collaboratives: à tout moment, un programme avec des coroutines exécute une seule de ses coroutines, et cette coroutine en cours d'exécution ne suspend son exécution que lorsqu'elle demande explicitement à être suspendue.
Le point est donc le suivant: les coroutines sont «collaboratives». Même dans un système multicœur, il n'y a qu'une seule coroutine en cours d'exécution à un moment donné (mais plusieurs threads peuvent s'exécuter en parallèle). Il n'y a pas de préemption entre les coroutines, la coroutine en cours d'exécution doit renoncer explicitement à l'exécution.
Pour " concurrency
", vous pouvez consulter la diapositive de Rob Pike :
La concurrence est la composition de calculs exécutés indépendamment.
Ainsi, lors de l'exécution de la coroutine A, elle passe le contrôle à la coroutine B.Ensuite, après un certain temps, la coroutine B renvoie le contrôle à la coroutine A. Puisqu'il existe une dépendance entre les coroutines, et qu'elles doivent s'exécuter en tandem, les deux coroutines ne sont pas concurrentes .
Je trouve la plupart des réponses trop techniques même s'il s'agit d'une question technique. J'ai eu du mal à comprendre le processus coroutin. Je l'ai en quelque sorte, mais je ne l'ai pas en même temps.
J'ai trouvé cette réponse ici très utile:
https://dev.to/thibmaek/explain-coroutines-like-im-five-2d9
Pour citer Idan Arye:
Pour bâtir sur votre histoire, je dirais ceci:
Vous commencez à regarder le dessin animé, mais c'est l'intro. Au lieu de regarder l'intro, vous passez au jeu et entrez dans le lobby en ligne - mais il faut 3 joueurs et seulement vous et votre sœur y êtes. Au lieu d'attendre qu'un autre joueur vous rejoigne, passez à vos devoirs et répondez à la première question. La deuxième question contient un lien vers une vidéo YouTube que vous devez regarder. Vous l'ouvrez - et il commence à se charger. Au lieu d'attendre qu'il se charge, vous revenez au dessin animé. L'intro est terminée, vous pouvez donc regarder. Maintenant, il y a des publicités - mais en attendant, un troisième joueur s'est joint à vous, donc vous passez au jeu Et ainsi de suite ...
L'idée est que vous ne changez pas simplement les tâches très rapidement pour donner l'impression que vous faites tout en même temps. Vous utilisez le temps que vous attendez pour que quelque chose se produise (IO) pour faire d'autres choses qui nécessitent votre attention directe.
Vérifiez définitivement le lien, il y a bien plus que je ne peux pas tout citer.
Coroutine est similaire au sous-programme / threads. La différence est qu'une fois qu'un appelant a appelé un sous-programme / threads, il ne reviendra jamais à la fonction d'appelant. Mais une coroutine peut revenir à l'appelant après avoir exécuté quelques morceaux de code permettant à l'appelant d'exécuter une partie de son propre code et de revenir au point de coroutine où il a arrêté l'exécution et de continuer à partir de là. c'est à dire. Une coroutine a plus d'un point d'entrée et de sortie
Fondamentalement, il existe deux types de Coroutines:
Kotlin implémente des coroutines sans pile - cela signifie que les coroutines n'ont pas leur propre pile, donc elles ne sont pas mappées sur le thread natif.
Ce sont les fonctions pour démarrer la coroutine:
launch{}
async{}
Vous pouvez en apprendre plus ici:
https://www.kotlindevelopment.com/deep-dive-coroutines/
https://blog.mindorks.com/what-are-coroutines-in-kotlin-bf4fecd476e9
Sur une note différente, dans la gevent
bibliothèque python est une coroutine
bibliothèque de mise en réseau basée qui vous offre des fonctionnalités similaires à des threads comme les demandes de réseau asynchrones, sans la surcharge de création et de destruction de threads. La coroutine
bibliothèque utilisée est greenlet
.
De Python Coroutine :
L'exécution des coroutines Python peut être suspendue et reprise à de nombreux points (voir coroutine). Dans le corps d'une fonction coroutine, les identifiants d'attente et asynchrones deviennent des mots clés réservés; les expressions en attente, async for et async with ne peuvent être utilisées que dans les corps de fonction coroutine.
Depuis Coroutines (C ++ 20)
Une coroutine est une fonction qui peut suspendre l'exécution pour reprendre plus tard . Les coroutines sont sans pile: elles suspendent l'exécution en retournant à l'appelant. Cela permet un code séquentiel qui s'exécute de manière asynchrone (par exemple pour gérer les E / S non bloquantes sans rappels explicites), et prend également en charge les algorithmes sur les séquences infinies calculées paresseux et d'autres utilisations.
Comparez avec la réponse des autres:
À mon avis, la partie reprise plus tard est une différence fondamentale, tout comme @ Twinkle.
Bien que de nombreux champs du document soient encore en cours de réalisation, cette partie est similaire à la plupart des réponses, à l'exception de @Nan Xiao.
Les coroutines, en revanche, sont collaboratives: à tout moment, un programme avec des coroutines exécute une seule de ses coroutines, et cette coroutine en cours d'exécution ne suspend son exécution que lorsqu'elle demande explicitement à être suspendue.
Puisqu'il est cité à partir de Program in Lua, il est peut-être lié à la langue (pas familier avec Lua actuellement), tous les documents ne mentionnent pas la seule partie.
La relation avec les concurrents:
Il y a une partie "Exécution" des Coroutines (C ++ 20). Trop long pour citer ici.
Outre le détail, il existe plusieurs états.
When a coroutine begins execution
When a coroutine reaches a suspension point
When a coroutine reaches the co_return statement
If the coroutine ends with an uncaught exception
When the coroutine state is destroyed either because it terminated via co_return or uncaught exception, or because it was destroyed via its handle
comme le commentaire de @Adam Arold sous la réponse de @ user217714. C'est la simultanéité.
Mais c'est différent du multithreading.
de std :: thread
Les threads permettent à plusieurs fonctions de s'exécuter simultanément. Les threads commencent leur exécution immédiatement lors de la construction de l'objet thread associé (en attente de tout retard de planification du système d'exploitation), en commençant par la fonction de niveau supérieur fournie comme argument de constructeur. La valeur de retour de la fonction de niveau supérieur est ignorée et si elle se termine en lançant une exception, std :: terminate est appelée. La fonction de niveau supérieur peut communiquer sa valeur de retour ou une exception à l'appelant via std :: promise ou en modifiant des variables partagées (qui peuvent nécessiter une synchronisation, voir std :: mutex et std :: atomic)
Comme il s'agit d'une concurrence, cela fonctionne comme le multithreading, surtout lorsque l'attente est inévitable (du point de vue du système d'exploitation), c'est aussi pourquoi cela prête à confusion.
Une coroutine est un type particulier de sous-programme. Plutôt que la relation maître-esclave entre un appelant et un sous-programme appelé qui existe avec des sous-programmes conventionnels, l'appelant et les coroutines appelées sont plus équitables.
Une coroutine est un sous-programme qui a plusieurs entrées et les contrôle lui-même - pris en charge directement dans Lua
Aussi appelé contrôle symétrique: l'appelant et les coroutines appelées sont sur une base plus égale
Un appel coroutine s'appelle un CV
La première reprise d'une coroutine est à son début, mais les appels suivants entrent au point juste après la dernière instruction exécutée dans la coroutine
Les coroutines se reprennent à plusieurs reprises, peut-être pour toujours
Les coroutines fournissent l'exécution quasi-simultanée des unités de programme (les coroutines); leur exécution est entrelacée, mais pas superposée
Je trouve qu'une explication de ce lien est assez simple. Aucune de ces réponses n'essaie d'expliquer la simultanéité par rapport au parallélisme, sauf le dernier point de cette réponse .
cité de "programmation Erlang", par Joe Armstrong, le légendaire:
un programme simultané peut s'exécuter potentiellement plus rapidement sur un ordinateur parallèle.
un programme simultané est un programme écrit dans un langage de programmation simultané. Nous écrivons des programmes simultanés pour des raisons de performances, d'évolutivité ou de tolérance aux pannes.
un langage de programmation simultané est un langage qui a des constructions de langage explicites pour écrire des programmes simultanés. Ces constructions font partie intégrante du langage de programmation et se comportent de la même manière sur tous les systèmes d'exploitation.
un ordinateur parallèle est un ordinateur qui possède plusieurs unités de traitement (CPU ou cœurs) qui peuvent fonctionner en même temps.
La simultanéité n'est donc pas la même chose que le parallélisme. Vous pouvez toujours écrire des programmes simultanés sur un ordinateur monocœur. Le planificateur de partage de temps vous fera sentir que votre programme s'exécute simultanément.
Le programme simultané a le potentiel de s'exécuter en parallèle dans un ordinateur parallèle mais n'est pas garanti . Le système d'exploitation ne peut vous donner qu'un seul noyau pour exécuter votre programme.
Par conséquent, la concurrence est un modèle logiciel d'un programme simultané qui ne signifie pas que votre programme peut s'exécuter physiquement en parallèle.
Le mot «coroutine» est composé de deux mots: «co» (coopérative) et «routines» (fonctions).
une. atteint-il la concurrence ou le parallélisme?
Pour être simple, discutons-en sur un ordinateur monocœur .
La concurrence est obtenue par des partages de temps à partir du système d'exploitation. Un thread exécute son code dans les intervalles de temps qui lui sont attribués sur le cœur du processeur. Il peut être préempté par OS. Il peut également donner le contrôle à OS.
Une coroutine, d'autre part, cède le contrôle à une autre coroutine dans le thread, pas à OS. Ainsi, toutes les coroutines d'un thread exploitent toujours le délai de ce thread sans céder le cœur du processeur à d'autres threads gérés par le système d'exploitation.
Par conséquent, vous pouvez penser que coroutine réalise des partages de temps par l'utilisateur et non par OS (ou quasi-parallélisme). Les coroutines s'exécutent sur le même noyau affecté au thread qui exécute ces coroutines.
Coroutine parvient-il au parallélisme? Si c'est du code lié au CPU, non. Comme les partages de temps, cela donne l'impression qu'ils fonctionnent en parallèle mais leurs exécutions sont entrelacées et ne se chevauchent pas. Si elle est liée aux IO, oui, elle atteint le parallèle par le matériel (périphériques IO) et non par votre code.
b. la différence avec l'appel de fonction?
Comme le montre la photo, il n'a pas besoin d'appeler return
pour changer de contrôle. Il peut céder sans return
. Une coroutine enregistre et partage l'état sur le cadre de fonction actuel (pile). Il est donc beaucoup plus léger que la fonction car vous n'avez pas à enregistrer les registres et les variables locales pour empiler et rembobiner la pile d'appels quand call ret
.
Je développerai la réponse de @ user21714. Les coroutines sont des chemins d'exécution indépendants qui ne peuvent pas s'exécuter simultanément. Ils dépendent d'un contrôleur - par exemple une python
bibliothèque de contrôleurs - pour gérer la commutation entre ces chemins. Mais pour que cela fonctionne, les coroutines elles-mêmes doivent invoquer yield
ou des structures similaires qui permettent de suspendre leur exécution.
Les threads s'exécutent à la place sur des ressources de calcul indépendantes et en parallèle les uns avec les autres. Puisqu'ils sont sur des ressources différentes, il n'est pas nécessaire d'appeler yield pour permettre aux autres chemins d'exécution de continuer.
Vous pouvez voir cet effet en démarrant un programme multithread - par exemple une jvm
application - dans lequel les huit de vos core i7
cœurs hyperthread sont utilisés: vous pourriez voir une utilisation de 797% dans Activity Monitor
ou Top
. Au lieu de cela, lors de l'exécution d'un python
programme typique - même avec coroutines
ou python threading
-, l'utilisation sera au maximum de 100%. C'est-à-dire une machine hyperthread.