Maison >développement back-end >Golang >Explication graphique détaillée du mécanisme de concurrence du langage Go

Explication graphique détaillée du mécanisme de concurrence du langage Go

尚
avant
2019-11-30 14:31:492320parcourir

Langage GoL'un des grands avantages par rapport à Java et aux autres est qu'il peut facilement écrire des programmes simultanés. Le langage Go dispose d'un mécanisme goroutine intégré. Grâce à goroutine, vous pouvez développer rapidement des programmes simultanés et mieux utiliser les ressources du processeur multicœur. Cet article apprend l'application de goroutine et sa mise en œuvre de planification.

Explication graphique détaillée du mécanisme de concurrence du langage Go

1. Prise en charge de la concurrence par le langage Go

Utiliser la programmation goroutine (recommandé : go tutoriel vidéo )

Utilisez le mot-clé go pour créer une goroutine. Placez la déclaration go avant une fonction qui doit être appelée, puis appelez et exécutez la fonction dans le même espace d'adressage, afin que la fonction soit exécutée en tant que thread concurrent indépendant. Ce genre de thread est appelé goroutine en langage Go.

L'utilisation de goroutine est la suivante :

//go 关键字放在方法调用前新建一个 goroutine 并执行方法体
go GetThingDone(param1, param2);
 
//新建一个匿名方法并执行
go func(param1, param2) {
}(val1, val2)
 
//直接新建一个 goroutine 并在 goroutine 中执行代码块
go {
    //do someting...
}

Parce que goroutine est parallèle dans un environnement CPU multicœur. Si un bloc de code est exécuté dans plusieurs goroutines, nous obtenons le parallélisme du code.

Si vous avez besoin de connaître l'exécution du programme, comment obtenir les résultats parallèles ? Il doit être utilisé conjointement avec le canal.

Utiliser des canaux pour contrôler la concurrence

Les canaux sont utilisés pour synchroniser les fonctions exécutées simultanément et leur fournir une sorte de mécanisme de communication de transmission de valeur.

Le type d'élément, le conteneur (ou le tampon) et la direction de transfert transitant par le canal sont spécifiés par l'opérateur "

Vous pouvez utiliser la fonction intégrée make pour allouer un canal :

i := make(chan int)       // by default the capacity is 0
s := make(chan string, 3) // non-zero capacity
 
r := make(<-chan bool)          // can only read from
w := make(chan<- []os.FileInfo) // can only write to

Configurer le runtime.GOMAXPROCS

Utilisez le code suivant pour définir explicitement s'il faut utiliser le multicœur Pour exécuter des tâches simultanées :

runtime.GOMAXPROCS()

Le nombre de GOMAXPROCS peut être alloué en fonction du nombre de tâches, mais il ne doit pas être supérieur au nombre de cœurs de processeur.

La configuration de l'exécution parallèle est plus adaptée aux scénarios gourmands en CPU et hautement parallèles. Si elle est gourmande en E/S, l'utilisation de plusieurs cœurs augmentera la perte de performances causée par la commutation du CPU.

Après avoir compris le mécanisme de concurrence du langage Go, examinons l'implémentation spécifique du mécanisme goroutine.

2. La différence entre le parallélisme et la concurrence

Processus, thread et processeur

Dans les systèmes d'exploitation modernes, les threads sont l'unité de base de la planification et de l'allocation du processeur, et les processus sont l'unité de base de la propriété des ressources. Chaque processus est composé d'un espace d'adressage virtuel privé, de code, de données et d'autres ressources système diverses. Un thread est une unité d'exécution au sein d'un processus. Chaque processus possède au moins un thread d'exécution principal, qui n'a pas besoin d'être créé activement par l'utilisateur mais est automatiquement créé par le système. Les utilisateurs créent d'autres threads dans l'application selon leurs besoins, et plusieurs threads s'exécutent simultanément dans le même processus.

Parallélisme et concurrence

Le parallélisme et la concurrence (Concurrency and Parallelism) sont deux concepts différents. Les comprendre est très important pour comprendre le modèle multi-thread.

Lors de la description de la concurrence ou du parallélisme d'un programme, cela doit être énoncé du point de vue d'un processus ou d'un thread.

Concurrence : de nombreux threads ou processus s'exécutent au cours d'une période donnée, mais un seul s'exécute à un moment donné. Plusieurs threads ou processus se disputent des tranches de temps et s'exécutent à tour de rôle

Parallèle. : Plusieurs threads ou processus s'exécutent sur une période et à un moment précis

Un programme non simultané n'a qu'une seule logique de contrôle verticale. À tout moment, le programme ne sera que dans une certaine position de cette logique de contrôle. , c'est-à-dire une exécution séquentielle. Si un programme est traité par plusieurs pipelines CPU en même temps à un certain moment, alors nous disons que le programme s'exécute en parallèle.

Le parallélisme nécessite une prise en charge matérielle. Les processeurs monocœurs ne peuvent fonctionner que simultanément, tandis que les processeurs multicœurs peuvent réaliser une exécution parallèle.

La concurrence est une condition nécessaire au parallélisme. Si un programme lui-même n'est pas concurrent, c'est-à-dire qu'il n'y a qu'une seule séquence d'exécution logique, alors nous ne pouvons pas lui permettre d'être traité en parallèle.

La concurrence n'est pas une condition suffisante pour le parallélisme. Si un programme concurrent est traité par un seul processeur (via le partage de temps), alors il n'est pas parallèle.

Par exemple, écrivez le programme de structure séquentielle le plus simple pour afficher "Hello World", qui n'est pas simultané. Si vous ajoutez plusieurs threads au programme et que chaque thread imprime un "Hello World", alors ces programmes sont. concurrent. Si un seul processeur est alloué à ce programme pendant l'exécution, ce programme concurrent n'est pas encore parallèle et doit être déployé sur un processeur multicœur pour obtenir le parallélisme du programme.

3. Plusieurs modèles multi-thread différents

Threads utilisateur et threads au niveau du noyau

L'implémentation des threads peut être divisée en deux catégories : les threads au niveau utilisateur (User-LevelThread, ULT) et les threads au niveau du noyau (Kemel-LevelThread, KLT). Les threads utilisateur sont sauvegardés par le code utilisateur et les threads du noyau sont sauvegardés par le noyau du système d'exploitation.

Modèle multi-thread

Le modèle multi-thread représente les différentes méthodes de connexion des threads au niveau de l'utilisateur et des threads au niveau du noyau.

(1) Modèle plusieurs-à-un (M : 1)

Mappez plusieurs threads au niveau de l'utilisateur sur un thread au niveau du noyau, et la gestion des threads est terminée dans l'espace utilisateur. Dans ce mode, les threads au niveau utilisateur sont invisibles (c'est-à-dire transparents) pour le système d'exploitation.

Explication graphique détaillée du mécanisme de concurrence du langage Go

Avantages : L'avantage de ce modèle est que le changement de contexte de thread se produit dans l'espace utilisateur, évitant ainsi le changement de mode, ce qui a un impact positif sur les performances.

Inconvénients : tous les threads sont basés sur une seule entité de planification du noyau, à savoir le thread du noyau, ce qui signifie qu'un seul processeur peut être utilisé. Ce n'est pas acceptable dans un environnement multiprocesseur. ne résout que le problème de concurrence, mais pas le problème parallèle. Si un thread tombe dans l'état du noyau en raison d'une opération d'E/S et que le thread d'état du noyau se bloque en attendant les données d'E/S, tous les threads seront bloqués. L'espace utilisateur peut également utiliser des E/S non bloquantes, mais en termes de performances et de complexité. ne peut être évité.

(2) Modèle un-à-un (1:1)

Mappe chaque thread au niveau de l'utilisateur à un thread au niveau du noyau.

Explication graphique détaillée du mécanisme de concurrence du langage Go

Chaque thread est planifié indépendamment par le planificateur du noyau, donc si un thread bloque, cela n'affectera pas les autres threads.

Avantages : Grâce à la prise en charge du matériel de processeur multicœur, le modèle de thread spatial du noyau prend en charge le véritable parallélisme. Lorsqu'un thread est bloqué, un autre thread est autorisé à continuer de s'exécuter, la capacité de concurrence est donc forte.

Inconvénients : chaque fois qu'un thread au niveau de l'utilisateur est créé, un thread au niveau du noyau doit être créé pour lui correspondre. Cela crée un thread relativement coûteux et affectera les performances de l'application.

(3) Modèle plusieurs-à-plusieurs (M : N)

Le rapport numérique des threads du noyau et des threads utilisateur est M : N. L'espace utilisateur du noyau combine les avantages du premier deux.

Explication graphique détaillée du mécanisme de concurrence du langage Go

Ce modèle nécessite que le planificateur de threads du noyau et le planificateur de threads de l'espace utilisateur interagissent. Essentiellement, plusieurs threads sont liés à plusieurs threads du noyau, ce qui fait que la plupart des changements de contexte de thread se produisent dans. l'espace utilisateur et plusieurs threads du noyau peuvent utiliser pleinement les ressources du processeur.

4. Planification de la mise en œuvre du mécanisme goroutine

Le mécanisme goroutine implémente le modèle de thread M:N, et le mécanisme goroutine est coroutine An. implémentation du planificateur intégré de Golang, qui permet à chaque processeur d'un processeur multicœur d'exécuter une coroutine.

La clé pour comprendre les principes du mécanisme goroutine est de comprendre l'implémentation du planificateur du langage Go.

Fonctionnement du planificateur

Il existe quatre structures importantes dans le langage Go qui prennent en charge l'ensemble de la mise en œuvre du planificateur, à savoir M, G, P et Sched. trois définitions se trouvent dans runtime.h et Sched est défini dans proc.c.

La structure planifiée est le planificateur, qui maintient des files d'attente qui stockent M et G et certaines informations d'état du planificateur.

La structure M est Machine, thread système, qui est géré par le système d'exploitation. Goroutine s'exécute sur M ; M est une grande structure qui maintient le cache mémoire des petits objets (mcache), exécution actuelle. Il y a beaucoup d'informations. sur les goroutines, les générateurs de nombres aléatoires, etc.

La structure P est Processeur, processeur Son objectif principal est d'exécuter une goroutine. Elle maintient une file d'attente goroutine, à savoir runqueue. Le processeur joue un rôle important pour nous permettre de passer de la planification N:1 à la planification M:N.

G est la structure de base de l'implémentation de la goroutine. Elle contient la pile, le pointeur d'instruction et d'autres informations importantes pour la planification de la goroutine, comme son canal bloqué.

Le nombre de processeurs est défini sur la valeur de la variable d'environnement GOMAXPROCS au démarrage, ou est défini en appelant la fonction GOMAXPROCS() au moment de l'exécution. Le nombre fixe de processeurs signifie que seuls les threads GOMAXPROCS exécutent du code go à tout moment.

Nous utilisons des triangles, des rectangles et des cercles pour représenter respectivement Machine Processor et Goroutine.

Explication graphique détaillée du mécanisme de concurrence du langage Go

Dans le scénario d'un processeur monocœur, toutes les goroutines s'exécutent dans le même thread système M. Chaque thread système M maintient à tout moment un processeur. il n'y a qu'une seule goroutine, et d'autres goroutines attendent dans la file d'attente. Une fois qu'une goroutine a fini d'exécuter sa propre tranche de temps, elle abandonne le contexte et retourne à la file d'attente. Dans le scénario des processeurs multicœurs, afin d'exécuter des goroutines, chaque thread du système M contiendra un processeur.

Explication graphique détaillée du mécanisme de concurrence du langage Go

Dans des circonstances normales, le planificateur planifiera selon le processus ci-dessus, mais les threads seront bloqués. Jetez un œil à la gestion du blocage des threads par goroutine.

Blocage de thread

Lorsque la goroutine en cours d'exécution est bloquée, par exemple lors d'un appel système, un autre thread système (M1) sera créé et le thread M actuel sera abandonner. Son processeur, P est transféré vers un nouveau thread pour s'exécuter.

Explication graphique détaillée du mécanisme de concurrence du langage Go

l'exécution de la file d'exécution est terminée

Lorsque l'une des files d'exécution du processeur est vide, aucune goroutine ne peut être programmée. Cela volera la moitié de la goroutine d'un autre contexte.

Explication graphique détaillée du mécanisme de concurrence du langage Go

5. Autres réflexions sur la mise en œuvre de la concurrence

Il y a beaucoup à apprendre sur la concurrence. mécanisme du langage Go Discuté, comme la différence entre le langage Go et l'implémentation de la concurrence Scala, la comparaison entre le modèle Golang CSP et Actor, etc.

Comprendre ces implémentations de mécanismes de concurrence peut nous aider à mieux développer des programmes concurrents et à optimiser les performances.

Concernant les trois modèles multi-threading, vous pouvez faire attention à l'implémentation du langage Java.

Nous savons que Java encapsule les différences dans le système d'exploitation sous-jacent via la JVM, et que différents systèmes d'exploitation peuvent utiliser différents modèles de thread. Par exemple, Linux et Windows peuvent utiliser un modèle un-à-un, et certains. les versions de Solaris et Unix peuvent utiliser un modèle plusieurs-à-plusieurs. La spécification JVM ne stipule pas l'implémentation spécifique du modèle multithread. Aucun des modèles 1:1 (threads du noyau), N:1 (threads d'état utilisateur) et M:N (mixtes) est acceptable. Lorsqu'il s'agit du modèle multithread du langage Java, il doit être implémenté pour une JVM spécifique. Par exemple, la VM HotSpot d'Oracle/Sun utilise le modèle de thread 1:1 par défaut.

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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer