Programmation asynchrone en Go : guide complet avec pratique

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Programmation asynchrone en Go : de la théorie à la pratique



Go (Golang) est réputé pour son support intégré de la programmation asynchrone. Contrairement à de nombreux autres langages où l'asynchronisme nécessite des bibliothèques complexes et des callbacks, Go propose des primitives élégantes et puissantes : les goroutines et les canaux (channels). Dans ce guide, nous allons décortiquer tous les concepts clés en pratique, afin que vous puissiez écrire du code parallèle efficace en Go.



Que sont les goroutines ?



Une goroutine est un fil d'exécution léger géré par le runtime de Go. Contrairement aux threads système, les goroutines consomment très peu de ressources (quelques kilo-octets de pile seulement), ce qui permet d'exécuter des milliers, voire des millions de goroutines simultanément.



Lancer une goroutine est très simple : il suffit d'ajouter le mot-clé go avant un appel de fonction :



package main

import ( "fmt" "time")

func sayHello() { fmt.Println("Bonjour depuis la goroutine !")}

func main() { go sayHello() // Lancement de la goroutine time.Sleep(time.Millisecond * 100) // Donne du temps à la goroutine pour s'exécuter fmt.Println("Fonction principale terminée")}


Remarque : si vous n'ajoutez pas time.Sleep, le programme peut se terminer avant que la goroutine ne commence à s'exécuter. Pour la synchronisation, il est préférable d'utiliser d'autres mécanismes que nous verrons ci-dessous.



Canaux (Channels) : transmission de données entre goroutines



Les canaux sont des « tuyaux » typés par lesquels les goroutines peuvent échanger des données. Un canal est créé avec la fonction make et l'opérateur chan. Les données sont envoyées dans le canal avec l'opérateur <-, et reçues du canal également avec <-, mais à gauche du canal.



Voyons un exemple avec un canal non bufferisé et un canal bufferisé :



package main

import "fmt"

func main() { // Canal non bufferisé (taille 0) ch := make(chan string) go func() { ch <- "Message depuis la goroutine" // Envoi de données }() msg := <-ch // Réception des données (bloque jusqu'à réception du message) fmt.Println(msg) // Canal bufferisé (taille 2) bufferedCh := make(chan int, 2) bufferedCh <- 10 bufferedCh <- 20 fmt.Println(<-bufferedCh) // 10 fmt.Println(<-bufferedCh) // 20}


Important : Un canal non bufferisé bloque l'expéditeur jusqu'à ce que le destinataire reçoive les données. Un canal bufferisé bloque l'expéditeur uniquement lorsque le tampon est plein.



Select : multiplexage des canaux



L'instruction select permet à une goroutine d'attendre plusieurs canaux à la fois et de traiter celui qui est prêt en premier. C'est l'équivalent de switch, mais pour les canaux. select est un outil puissant pour créer des timeouts, annuler des opérations et effectuer des vérifications non bloquantes.



package main

import ( "fmt" "time")

func main() { ch1 := make(chan string) ch2 := make(chan string) // Goroutine 1 : envoie des données après 1 seconde go func() { time.Sleep(1 * time.Second) ch1 <- "Données du canal 1" }() // Goroutine 2 : envoie des données après 2 secondes go func() { time.Sleep(2 * time.Second) ch2 <- "Données du canal 2" }() select { case msg1 := <-ch1: fmt.Println("Reçu :", msg1) case msg2 := <-ch2: fmt.Println("Reçu :", msg2) case <-time.After(500 * time.Millisecond): fmt.Println("Timeout ! Aucun canal n'a répondu en 500 ms") }}


Dans cet exemple, select déclenchera le timeout, car aucun canal n'est prêt en 500 ms. Si nous avions retiré le timeout, le programme aurait attendu le premier canal prêt.

Blogs

Book Recommendations