Maison > Article > développement back-end > À propos de la planification des coroutines Golang
Ce qui suit est une introduction à la planification de coroutines Golang à partir de la colonne tutoriel Golang J'espère que cela sera utile aux amis dans le besoin !
Jetons un coup d'œil à la planification des coroutines de Golang
Groutine peut avoir une implémentation simultanée puissante via le modèle de planification GPM. Expliquons le modèle de planification goroutine ci-dessous.
Le planificateur de Go contient trois structures importantes : M, P, G
M : M est une encapsulation de threads au niveau du noyau, et le nombre correspond au réel Numéro de CPU, un M est un thread et une goroutine s'exécute sur M ; M est une grande structure qui conserve de nombreuses informations telles que le cache de mémoire de petits objets (mcache), la goroutine en cours d'exécution, le générateur de nombres aléatoires, etc.
G : représente une goroutine, qui possède sa propre pile, son pointeur d'instruction et d'autres informations (canaux en attente, etc.) pour la planification.
P : Le nom complet de P est Processor. Son objectif principal est d'exécuter la goroutine. Chaque objet Processeur a une LRQ (Local Run Queue). Les objets Goroutine non alloués sont stockés dans la GRQ (Global Run Queue), en attente d'être alloués à un certain P dans la LRQ. Chaque LRQ contient plusieurs objets Goroutines créés par l'utilisateur.
Golang utilise un modèle multi-threading. Plus en détail, il s'agit d'un modèle de thread à deux niveaux, mais il encapsule les threads système (threads au niveau du noyau) et expose une goroutine de coroutine légère (threads au niveau de l'utilisateur). sont destinés aux utilisateurs, et la planification des threads au niveau de l'utilisateur vers les threads au niveau du noyau est gérée par le runtime de Golang, et la logique de planification est transparente pour le monde extérieur. L'avantage de goroutine est que le changement de contexte est effectué dans l'état utilisateur complet. Il n'est pas nécessaire de basculer entre l'état utilisateur et l'état du noyau aussi souvent que les threads, ce qui permet d'économiser des ressources.
Comme le montre la figure ci-dessus, il y a 2 threads physiques M, chaque M a un processeur P, et chacun Il y a une goroutine en cours d'exécution.
Le nombre de P peut être défini via GOMAXPROCS(), qui représente en fait la concurrence réelle, c'est-à-dire le nombre de goroutines pouvant s'exécuter en même temps.
Les goroutines grises sur la photo ne fonctionnent pas, mais sont prêtes, en attente d'être programmées. P maintient cette file d'attente (appelée runqueue). Dans le langage Go, il est facile de démarrer une goroutine : fonction just go Ainsi, chaque fois qu'une instruction go est exécutée, une
goroutine est ajoutée à la fin de la file d'attente runqueue. , au point de planification suivant, une goroutine est retirée de la file d'attente (comment décider quelle goroutine prendre ?) et exécutée.
S'il ne l'obtient pas, il mettra la goroutine dans un environnement global
runqueue, puis dormir tout seul (mis dans le cache des threads). All P vérifiera également périodiquement global
runqueue et exécutez la goroutine dedans, sinon la goroutine sur la file d'exécution globale ne sera jamais exécutée.
1. Le nombre de P :
2. Le nombre de M :
Il n'y a pas de relation absolue entre le nombre de M et P. Si un M est bloqué, P créera ou passera à un autre M. Par conséquent, même si le nombre par défaut de P est 1, de nombreux M peut être créé.
3. Lorsque P est créé : Après avoir déterminé le nombre maximum n de P, le système d'exécution créera n Ps en fonction de ce nombre.
4. Lorsque M est créé : il n'y a pas assez de M pour s'associer à P et y exécuter le G exécutable. Par exemple, si tous les M sont bloqués à ce moment-là et qu'il y a encore de nombreuses tâches prêtes dans P, il recherchera un M inactif. S'il n'y a pas de M inactif, un nouveau M sera créé.
Lorsque M est bloqué en raison d'un appel système (lorsque G exécuté sur M entre dans un appel système), M et P seront séparés s'il y a des tâches dans la file d'attente prête de P à ce moment,
P. Il s'associera à un M inactif ou créera un M pour l'association. (C'est-à-dire que go ne gère pas le blocage des E/S comme libtask ? Pas sûr.)
Si toutes les tâches de la file d'attente prête d'un P ont été exécutées, alors P essaiera d'obtenir G des autres P file d'attente prête. Retirez-en une partie et placez-la dans sa propre file d'attente prête pour vous assurer que la file d'attente prête de chaque P contient des tâches qui peuvent être exécutées.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!