Qu'est-ce qu'une coroutine?

204

Qu'est-ce qu'une coroutine? Comment sont-ils liés à la concurrence?

yesraaj
la source
2
Le code simultané ne doit pas nécessairement s'exécuter en "parallèle" (n'introduisons pas de nouveaux termes).
lucid_dreamer
2
J'ai écrit une bibliothèque coroutine avec C standard, prenant en charge les messages GUI select / poll / eplll / kqueue / iocp / Win pour Linux, BSD et Windows. C'est un projet open-source sur github.com/acl-dev/libfiber . Les conseils seront les bienvenus.
ShuXin Zheng
Plus d'informations intéressantes ici: stackoverflow.com/q/16951904/14357
dépenser
Je peux imaginer que cette question sera rejetée si elle est posée à l'ère actuelle. Vous ne savez pas pourquoi il y a une si grande différence de perception de la communauté par rapport à avant?
tnkh
une coroutine est une fonction qui peut suspendre son exécution avant d'atteindre le retour, et elle peut indirectement passer le contrôle à une autre coroutine pendant un certain temps.
hassanzadeh.sd

Réponses:

138

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.

user21714
la source
19
Quelle est la différence entre appeler une fonction directement et céder à partir d'une coroutine en enveloppant cette fonction dans cette coroutine?
Ming Li
3
Il serait peut-être préférable d'expliquer que ces deux concepts ne sont donc pas vraiment «orthogonaux» dans ce contexte. Vous pouvez certainement dessiner comment les deux concepts sont similaires. L'idée de passer le contrôle entre deux ou plusieurs choses est très similaire.
steviejay
8
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.
Adam Arold
@steviejay orthogonal = Not similar to each other?
tonix
1
@tonix On m'a dit que cela orthogonalsignifie "indépendant les uns des autres".
Rick
77

De la programmation dans Lua , Coroutinessection " ":

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 .

Nan Xiao
la source
6
Les coroutines ne s'exécutent pas indépendamment. Ils se relaient, chacun attendant que l'autre fasse une partie du travail. Ils se coordonnent activement les uns avec les autres. C'est l'opposé de la définition de la concurrence par Rob Pikes.
Erick G.Hagstrom
2
@ ErickG.Hagstrom: Bien qu'ils ne s'exécutent pas indépendamment, la logique de chaque coroutine peut être indépendante, non? Si c'est le cas, c'est comme un système d'exploitation non préemptif s'exécutant sur un processeur monocœur, un processus doit abandonner le processeur pour laisser les autres tâches s'exécuter.
Nan Xiao
6
Il y a une différence entre abandonner le CPU pour laisser une autre tâche s'exécuter et dire à un autre processus spécifique qu'il est temps de l'exécuter. Les coroutines font ce dernier. Ce n'est pas indépendant du tout.
Erick G.Hagstrom
7
@ChrisClark Je suis d'accord avec vous. Les coroutines sont simultanées. Voici une citation de wikipedia: Les coroutines sont très similaires aux threads. Cependant, les coroutines sont multitâches en coopération, tandis que les threads sont généralement multitâches de manière préventive. Cela signifie qu'ils fournissent la simultanéité mais pas le parallélisme .
smwikipedia
3
Et: le multitâche coopératif, également connu sous le nom de multitâche non préemptif, est un style de multitâche informatique dans lequel le système d'exploitation n'initie jamais un changement de contexte d'un processus en cours d'exécution à un autre processus. Au lieu de cela, les processus cèdent volontairement le contrôle périodiquement ou lorsqu'ils sont inactifs ou bloqués logiquement afin de permettre l'exécution simultanée de plusieurs applications.
smwikipedia
47

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.

mr1031011
la source
6
Illustration très simple et directe. +1 pour cela.
Taslim Oseni
grande illustration. J'ai construit une histoire similaire - en faisant la queue en attendant de récupérer un paquet. mais pour aujourd'hui, le vôtre est beaucoup plus réaliste, qui fait la queue quand il y a des livraisons porte à porte? Lol
apolak
1
Voilà une explication impressionnante. D'après la citation elle-même, c'est super clair.
Farruh Habibullaev
15

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

Scintillement
la source
Il n'est pas si similaire aux threads - qui s'exécutent indépendamment et simultanément (cœurs séparés en parallèle). De plus, la comparaison des sous-programmes échoue en ce sens qu'il existe plusieurs chemins d'exécution indépendants et qu'ils ne se renvoient pas les résultats.
javadba
11
  • Les coroutines sont d'excellentes fonctionnalités disponibles en langage Kotlin
  • Les coroutines sont une nouvelle façon d'écrire du code asynchrone et non bloquant (et bien plus encore)
  • Coroutine sont des fils légers. Un thread léger signifie qu'il ne correspond pas au thread natif, il ne nécessite donc pas de changement de contexte sur le processeur, ils sont donc plus rapides.
  • il ne mappe pas sur le thread natif
  • Les coroutines et les threads sont tous deux multitâches. Mais la différence est que les threads sont gérés par le système d'exploitation et les coroutines par les utilisateurs.

Fondamentalement, il existe deux types de Coroutines:

  1. Stackless
  2. Empilable

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

Dhaval Jivani
la source
1
Bonne réponse! Utile pour les développeurs Kotlin et Android.
Malwinder Singh
5

Sur une note différente, dans la geventbibliothèque python est une coroutinebibliothè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 coroutinebibliothèque utilisée est greenlet.

joseph
la source
2

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.

Shihe Zhang
la source
1

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

Exemple 1 Exemple2

BoraKurucu
la source
1

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 .

  1. qu'est-ce qui est simultané (programme)?

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.

  1. coroutine et simultanéité

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?

entrez la description de l'image ici

Comme le montre la photo, il n'a pas besoin d'appeler returnpour 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.

Izana
la source
0

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 pythonbibliothèque de contrôleurs - pour gérer la commutation entre ces chemins. Mais pour que cela fonctionne, les coroutines elles-mêmes doivent invoquer yieldou 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 jvmapplication - dans lequel les huit de vos core i7cœurs hyperthread sont utilisés: vous pourriez voir une utilisation de 797% dans Activity Monitorou Top. Au lieu de cela, lors de l'exécution d'un pythonprogramme typique - même avec coroutinesou python threading-, l'utilisation sera au maximum de 100%. C'est-à-dire une machine hyperthread.

javadba
la source