la programmation

Concurrence en Go: Guide Complet

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 :

  1. 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 :
go
go 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.

  1. 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 :
go
monCanal := make(chan typeDeDonnée) monCanal <- maDonnée // Envoie de données sur le canal
  1. 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 :
go
var 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 :

go
package 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 :

go
go 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 :

go
monCanal := make(chan typeDeDonnée)

Pour envoyer des données sur un canal, on utilise l'opérateur <- :

go
monCanal <- maDonnée // Envoie de données sur le canal

Et pour recevoir des données depuis un canal :

go
maDonné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 :

go
var 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 :

go
package 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.

Bouton retour en haut de la page