Les piscines goroutine de go-langs sont-elles juste des discussions vertes?

47

Le commentateur propose ici les critiques suivantes sur les fils verts:

J'ai d'abord été vendu sur le modèle N: M comme moyen d'avoir une programmation événementielle sans l'enfer de callback. Vous pouvez écrire un code qui ressemble à un ancien code de procédure, mais il y a une magie qui utilise la commutation de tâches de l'espace utilisateur chaque fois que quelque chose se bloque. Super. Le problème est que nous finissons par résoudre la complexité avec plus de complexité. swapcontext () et sa famille sont assez distants, la complexité provient d’autres endroits inattendus.

Tout à coup, vous êtes obligé d'écrire un planificateur en espace utilisateur et de deviner ce qui est vraiment difficile d'écrire un planificateur qui fera un meilleur travail que les planifications de Linux, qui nécessite de nombreuses années d'efforts. Maintenant, vous voulez que votre planification gère N threads verts en M threads physiques, de sorte que vous devez vous préoccuper de la synchronisation. La synchronisation engendre des problèmes de performances, de sorte que vous démarrez maintenant le nouveau trou de lapin sans verrou. Construire un ordonnanceur hautement concurrentiel correct n’est pas une tâche facile.

Une autre critique est ici :

Un seul processus simulant plusieurs threads pose de nombreux problèmes. L'un d'eux est que tous les faux fils stagnent sur n'importe quel défaut de page.

Ma question est - sont go-lang goroutines de (pour un pool par défaut) seulement des fils verts? Si oui, répondent-ils aux critiques ci-dessus?

oeil de faucon
la source

Réponses:

67

Je ne suis qu'un utilisateur occasionnel de Go, prenez donc ce qui suit avec un grain de sel.

Wikipedia définit les threads verts comme des "threads planifiés par une machine virtuelle (VM) et non nativement par le système d'exploitation sous-jacent". Les threads verts émulent des environnements multithreads sans s'appuyer sur aucune fonctionnalité native du système d'exploitation. Ils sont gérés dans l'espace utilisateur au lieu de l'espace du noyau, ce qui leur permet de fonctionner dans des environnements ne prenant pas en charge les threads natifs.

Go (ou plus exactement les deux implémentations existantes) est un langage produisant uniquement du code natif - il n’utilise pas de machine virtuelle. En outre, le planificateur dans les implémentations d'exécution actuelles repose sur des threads au niveau du système d'exploitation (même lorsque GOMAXPROCS = 1). Je pense donc que parler de fils verts pour le modèle Go est un peu abusif.

Les gens de Go ont inventé le terme goroutine en particulier pour éviter la confusion avec d'autres mécanismes de concurrence (tels que des routines ou des threads ou des processus légers).

Bien entendu, Go prend en charge un modèle de thread M: N, mais il semble beaucoup plus proche du modèle de processus Erlang que du modèle de thread vert Java.

Voici quelques avantages du modèle Go par rapport aux threads verts (tels qu’implémentés dans les premières machines virtuelles Java):

  • Plusieurs cœurs ou processeurs peuvent être utilisés efficacement, de manière transparente pour le développeur. Avec Go, le développeur doit s’occuper de la concurrence. Le runtime va s'occuper du parallélisme. Les implémentations de threads verts Java n'ont pas évolué sur plusieurs cœurs ou processeurs.

  • Les appels système et C ne bloquent pas le planificateur (tous les appels système, pas seulement ceux prenant en charge les E / S multiplexées dans les boucles d’événements). Les implémentations de threads verts peuvent bloquer l’ensemble du processus lorsqu’un appel système bloquant est effectué.

  • Copier ou segmenter des piles. Dans Go, il n'est pas nécessaire de fournir une taille de pile maximale pour le goroutine. La pile croît progressivement au besoin. L'une des conséquences est qu'un goroutine ne nécessite pas beaucoup de mémoire (4 Ko à 8 Ko), de sorte qu'un très grand nombre d'entre eux peuvent être générés avec bonheur. L'utilisation de Goroutine peut donc être omniprésente.

Maintenant, pour répondre aux critiques:

  • Avec Go, vous n'avez pas besoin d'écrire de planificateur d'espace utilisateur: il est déjà fourni avec le runtime. C'est un logiciel complexe, mais c'est le problème des développeurs de Go, pas des utilisateurs de Go. Son utilisation est transparente pour les utilisateurs de Go. Parmi les développeurs de Go, Dmitri Vyukov est un expert en programmation lockfree / waitfree, et il semble être particulièrement intéressé par la résolution des éventuels problèmes de performances du planificateur. L’implémentation actuelle du planificateur n’est pas parfaite, mais elle va s’améliorer.

  • La synchronisation entraîne des problèmes de performance et de complexité: cela est également partiellement vrai avec Go. Notez cependant que le modèle Go tente de promouvoir l'utilisation de canaux et une décomposition propre du programme dans des goroutines simultanées afin de limiter la complexité de la synchronisation (c'est-à-dire le partage de données par communication au lieu de partager la mémoire pour communiquer). En passant, l'implémentation Go de référence fournit un certain nombre d'outils pour résoudre les problèmes de performances et de concurrence, tels qu'un profileur et un détecteur de course .

  • En ce qui concerne les défauts de page et les "simulations de threads multiples", veuillez noter que Go peut planifier goroutine sur plusieurs threads système. Lorsqu'un thread est bloqué pour une raison quelconque (erreur de page, blocage d'appels système), cela n'empêche pas les autres threads de continuer à planifier et d'exécuter d'autres goroutines. Maintenant, il est vrai qu'une erreur de page bloquera le thread du système d'exploitation, avec tous les goroutines supposées être planifiées sur ce thread. Cependant, dans la pratique, la mémoire du segment Go n'est pas censée être remplacée. Ce serait la même chose en Java: les langages ramassés ne tiennent pas très bien la mémoire virtuelle de toute façon. Si votre programme doit gérer les erreurs de page de manière élégante, c'est probablement parce qu'il doit gérer de la mémoire morte. Dans ce cas,

Ainsi, IMO, les goroutines ne sont pas des fils verts, et le langage Go et la mise en œuvre actuelle répondent principalement à ces critiques.

Didier Spezia
la source
1
Une réponse excellente et détaillée à la question :)
Tuxdude
1
J'aime cette réponse, mais avez-vous des références sur comment / quand les threads OS sont créés?
Lars
1
L'un des plus gros inconvénients de Go Language est qu'il crée un thread de noyau pour chaque appel système bloquant!
user1870400
8
Notez que l'article sur les «threads verts» sur Wikipedia a été modifié pour indiquer «les threads planifiés par une bibliothèque d'exécution ou une machine virtuelle (VM)»; ce qui signifie que selon cette définition, votre réponse ne serait plus correcte, car le moteur d'exécution Go effectue la planification / gestion. Je pense qu'il est plus utile de définir les threads verts en tant que threads d'espace utilisateur contrastant avec les threads OS. Et puis, oui, les goroutines sont des fils verts à coup sûr.
mknecht
1
2ème que @mknecht. Il ne s'agit pas de la machine virtuelle, mais de l'exécution. Et Go a définitivement un runtime. (qui gère le modèle de thread et la récupération de place).
Tim Harper