La mise en œuvre de plusieurs fonctions en parallèle à l’aide de la fonctionnalité de concurrence, ou concurrency, dans le langage Go (également appelé Golang) est une pratique courante pour exploiter efficacement les ressources matérielles disponibles, telles que les processeurs multi-cœurs, et accélérer l’exécution des programmes. En Go, la concurrence est prise en charge nativement par le biais des goroutines et des canaux, ce qui permet de créer des programmes concurrents de manière efficace et sûre.
Pour mettre en œuvre plusieurs fonctions en parallèle en utilisant la concurrence en Go, vous pouvez suivre les étapes suivantes :

- Utilisation de Goroutines : Les goroutines sont des tâches légères gérées par le runtime Go. Elles permettent d’exécuter des fonctions de manière concurrente. Pour démarrer une nouvelle goroutine, vous pouvez simplement préfixer l’appel à la fonction avec le mot-clé
go
. Par exemple :
gogo maFonction()
Cela démarrera l’exécution de maFonction()
dans une nouvelle goroutine, qui s’exécutera de manière concurrente par rapport au reste du programme.
- Communication entre Goroutines : Pour partager des données entre différentes goroutines ou pour synchroniser leur exécution, Go fournit des canaux (channels). Les canaux permettent l’échange de données de manière sûre et synchronisée entre goroutines. Vous pouvez créer un canal en utilisant le mot-clé
make
et les opérateurs<-
pour envoyer et recevoir des données. Voici un exemple de création d'un canal et d'envoi de données :
gomonCanal := make(chan typeDeDonnée)
monCanal <- maDonnée // Envoie de données sur le canal
- Attente de la fin de l'exécution : Pour attendre la fin de l'exécution de toutes les goroutines créées, vous pouvez utiliser les WaitGroups. Une WaitGroup est un compteur qui permet de suivre le nombre de goroutines en cours d'exécution. Vous pouvez l'incrémenter avant le lancement d'une goroutine et le décrémenter une fois qu'elle est terminée. Voici un exemple d'utilisation :
govar wg sync.WaitGroup
wg.Add(1) // Incrémente le compteur
go func() {
defer wg.Done() // Décrémente le compteur à la fin de l'exécution
// Code de la goroutine
}()
wg.Wait() // Attend la fin de toutes les goroutines
En combinant ces concepts, vous pouvez exécuter plusieurs fonctions en parallèle en Go. Voici un exemple complet illustrant ces concepts :
gopackage main
import (
"fmt"
"sync"
)
func maFonction(id int, wg *sync.WaitGroup) {
defer wg.Done() // Décrémente le compteur à la fin de l'exécution
fmt.Printf("Goroutine %d\n", id)
}
func main() {
var wg sync.WaitGroup
// Lancement de plusieurs goroutines
for i := 1; i <= 3; i++ {
wg.Add(1) // Incrémente le compteur
go maFonction(i, &wg)
}
// Attente de la fin de toutes les goroutines
wg.Wait()
fmt.Println("Toutes les goroutines ont terminé leur exécution.")
}
Dans cet exemple, maFonction
est exécutée en parallèle dans trois goroutines différentes, et la fonction main
attend ensuite la fin de toutes les goroutines grâce à la WaitGroup.
En suivant ces étapes et en comprenant les concepts de base de la concurrence en Go, vous pouvez efficacement mettre en œuvre plusieurs fonctions en parallèle dans vos programmes.
Plus de connaissances
Bien sûr, plongeons un peu plus dans les détails de la concurrence en Go.
Goroutines :
Les goroutines sont des tâches légères qui sont gérées par le runtime Go. Contrairement aux threads traditionnels, les goroutines sont gérées par le runtime Go lui-même, ce qui les rend plus efficaces en termes de mémoire et de gestion des ressources. Vous pouvez en créer des milliers, voire des dizaines de milliers, sans subir de pénalité de performances significative.
L'utilisation des goroutines est simple : il suffit de préfixer l'appel à une fonction avec le mot-clé go
. Par exemple :
gogo maFonction()
Cela démarrera l'exécution de maFonction()
dans une nouvelle goroutine, qui s'exécutera de manière concurrente par rapport au reste du programme.
Canaux (Channels) :
Les canaux sont des structures de données permettant la communication entre goroutines de manière sûre et synchronisée. Ils facilitent l'échange de données et la coordination entre différentes parties du programme. Les canaux peuvent être utilisés pour envoyer et recevoir des valeurs de manière bidirectionnelle ou unidirectionnelle.
Pour créer un canal, on utilise le mot-clé make
suivi du type de données à véhiculer :
gomonCanal := make(chan typeDeDonnée)
Pour envoyer des données sur un canal, on utilise l'opérateur <-
:
gomonCanal <- maDonnée // Envoie de données sur le canal
Et pour recevoir des données depuis un canal :
gomaDonnée := <-monCanal // Réception de données depuis le canal
Attente de la fin de l'exécution :
Pour attendre la fin de l'exécution de toutes les goroutines créées, on peut utiliser les WaitGroups. Une WaitGroup est un compteur qui permet de suivre le nombre de goroutines en cours d'exécution. On l'incrémente avant le lancement d'une goroutine et on le décrémente une fois qu'elle est terminée.
Voici comment utiliser une WaitGroup :
govar wg sync.WaitGroup
wg.Add(1) // Incrémente le compteur
go func() {
defer wg.Done() // Décrémente le compteur à la fin de l'exécution
// Code de la goroutine
}()
wg.Wait() // Attend la fin de toutes les goroutines
Exemple complet :
Voici un exemple plus complet illustrant l'utilisation des goroutines, des canaux et des WaitGroups en Go :
gopackage main
import (
"fmt"
"sync"
)
func maFonction(id int, wg *sync.WaitGroup, canal chan int) {
defer wg.Done() // Décrémente le compteur à la fin de l'exécution
canal <- id // Envoie l'ID sur le canal
}
func main() {
var wg sync.WaitGroup
canal := make(chan int)
// Lancement de plusieurs goroutines
for i := 1; i <= 3; i++ {
wg.Add(1) // Incrémente le compteur
go maFonction(i, &wg, canal)
}
// Attente de la fin de toutes les goroutines
wg.Wait()
close(canal)
// Lecture des données depuis le canal
for id := range canal {
fmt.Println("Goroutine", id, "a terminé.")
}
fmt.Println("Toutes les goroutines ont terminé leur exécution.")
}
Cet exemple crée trois goroutines, chacune envoyant son ID à travers un canal. La fonction main
attend ensuite la fin de toutes les goroutines à l'aide de la WaitGroup et lit les données du canal une fois qu'elles ont toutes terminé.
En combinant ces concepts, vous pouvez créer des programmes Go concurrents efficaces et sûrs. La concurrence en Go est un outil puissant pour tirer parti des architectures multi-cœurs et écrire des programmes performants et réactifs.