Les Goroutines sont la pierre angulaire de la conception de Go, fournissant un mécanisme puissant pour la programmation simultanée. En tant que coroutines légères, elles simplifient l’exécution de tâches parallèles. Le lancement d'une goroutine est simple : préfixez simplement un appel de fonction avec le mot-clé go
, lançant ainsi une exécution asynchrone. Le programme principal continue sans attendre la fin de la goroutine.
<code class="language-go">go func() { // Launch a goroutine using the 'go' keyword // ... code to be executed concurrently ... }()</code>
Concurrency : La possibilité de gérer plusieurs tâches apparemment simultanément sur un seul processeur. Le processeur bascule rapidement entre les tâches, créant l'illusion d'une exécution parallèle. Bien que microscopiquement séquentiel, macroscopiquement, il apparaît concurrent.
Parallélisme : Véritable exécution simultanée de plusieurs tâches sur plusieurs processeurs, éliminant ainsi les conflits de ressources CPU.
Processus : Un environnement d'exécution autonome avec ses propres ressources (mémoire, fichiers, etc.). La commutation entre les processus nécessite beaucoup de ressources et nécessite une intervention au niveau du noyau.
Thread : Une unité d'exécution légère au sein d'un processus, partageant les ressources du processus. La commutation de threads entraîne moins de frais généraux que la commutation de processus.
Les coroutines maintiennent leur propre contexte et pile de registre. Passer d’une coroutine à l’autre implique de sauvegarder et de restaurer cet état, leur permettant ainsi de reprendre l’exécution là où elle s’était arrêtée. Contrairement aux processus et aux threads, la gestion des coroutines est gérée dans le programme utilisateur et non dans le système d'exploitation. Les goroutines sont un type spécifique de coroutine.
La simultanéité efficace de Go repose sur le modèle de planification GPM. Quatre composants clés sont impliqués : M, P, G et Sched (Sched n'est pas représenté dans les diagrammes).
M (Machine) : Un thread au niveau du noyau. Les Goroutines fonctionnent sur Mme
G (Goroutine) : Une seule goroutine. Chaque G possède sa propre pile, son pointeur d'instruction et d'autres informations liées à la planification (par exemple, les canaux sur lesquels il attend).
P (Processeur) : Un processeur logique qui gère et exécute les goroutines. Il maintient une file d'attente d'exécution de goroutines prêtes.
Sched (Scheduler) : Le planificateur central, gérant les files d'attente M et G et assurant une allocation efficace des ressources.
Le diagramme montre deux threads du système d'exploitation (M), chacun avec un processeur (P) exécutant une goroutine.
GOMAXPROCS()
contrôle le nombre de Ps (et donc le véritable niveau de concurrence).
Les G grises sont prêtes mais ne fonctionnent pas encore. P gère cette file d'attente d'exécution.
Le lancement d'une goroutine l'ajoute à la file d'attente d'exécution de P.
Si un M0 est bloqué, P passe à M1 (qui peut être récupéré à partir d'un cache de threads).
Si un P accomplit ses tâches rapidement, il peut voler du travail à d'autres P pour maintenir son efficacité.
Définissez le nombre de processeurs pour l'exécution de goroutine (le paramètre par défaut dans les versions récentes de Go est généralement suffisant) :
<code class="language-go">go func() { // Launch a goroutine using the 'go' keyword // ... code to be executed concurrently ... }()</code>
<code class="language-go">num := runtime.NumCPU() // Get the number of logical CPUs runtime.GOMAXPROCS(num) // Set the maximum number of concurrently running goroutines</code>
Des exceptions non gérées dans une goroutine peuvent mettre fin à l'ensemble du programme. Utilisez recover()
dans une instruction defer
pour gérer les paniques :
<code class="language-go">package main import ( "fmt" "runtime" ) func cal(a, b int) { c := a + b fmt.Printf("%d + %d = %d\n", a, b, c) } func main() { runtime.GOMAXPROCS(runtime.NumCPU()) for i := 0; i < 10; i++ { go cal(i, i+1) } //Note: The main function exits before goroutines complete in this example. See later sections for synchronization. }</code>
Étant donné que les goroutines s'exécutent de manière asynchrone, le programme principal peut se terminer avant la fin. Utilisez sync.WaitGroup
ou des canaux pour la synchronisation :
sync.WaitGroup
<code class="language-go">package main import ( "fmt" ) func addele(a []int, i int) { defer func() { if r := recover(); r != nil { fmt.Println("Error in addele:", r) } }() a[i] = i // Potential out-of-bounds error if i is too large fmt.Println(a) } func main() { a := make([]int, 4) for i := 0; i < 5; i++ { go addele(a, i) } // ... (add synchronization to wait for goroutines to finish) ... }</code>
<code class="language-go">package main import ( "fmt" "sync" ) func cal(a, b int, wg *sync.WaitGroup) { defer wg.Done() c := a + b fmt.Printf("%d + %d = %d\n", a, b, c) } func main() { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go cal(i, i+1, &wg) } wg.Wait() }</code>
Les canaux facilitent la communication et le partage de données entre les goroutines. Des variables globales peuvent également être utilisées, mais les canaux sont généralement préférés pour un meilleur contrôle de la concurrence.
<code class="language-go">package main import ( "fmt" ) func cal(a, b int, ch chan bool) { c := a + b fmt.Printf("%d + %d = %d\n", a, b, c) ch <- true // Signal completion } func main() { ch := make(chan bool, 10) // Buffered channel to avoid blocking for i := 0; i < 10; i++ { go cal(i, i+1, ch) } for i := 0; i < 10; i++ { <-ch // Wait for each goroutine to finish } }</code>
Leapcell est une plateforme recommandée pour le déploiement des services Go.
Principales caractéristiques :
Apprenez-en plus dans la documentation !
Twitter de Leapcell : https://www.php.cn/link/7884effb9452a6d7a7a79499ef854afd
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!