Concurrence et multithreading en Go : niveau avancé

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

Concurrence et multithreading en Go : niveau avancé

\n\n

La concurrence (concurrency) est l'une des caractéristiques clés du langage Go, intégrée dans son architecture au niveau du runtime et de la syntaxe. Contrairement à de nombreux autres langages où le multithreading est implémenté via des bibliothèques complexes et nécessite une gestion manuelle des threads, Go propose des goroutines légères et des canaux (channels). Au niveau avancé, il ne s'agit pas simplement de lancer plusieurs tâches, mais de les synchroniser correctement, d'éviter les conditions de course et d'utiliser efficacement les ressources système. Dans cet article, nous aborderons les techniques avancées de travail avec la concurrence en Go.

\n\n

1. Goroutines : gestion du cycle de vie et priorités

\n\n

Les goroutines sont des threads légers gérés par le runtime de Go. Elles sont lancées à l'aide du mot-clé go. Cependant, au niveau avancé, il est nécessaire de comprendre comment contrôler leur exécution et leur terminaison.

\n\n

Lancement de base d'une goroutine :

\n
package main\n\nimport (\n    "fmt"\n    "time"\n)\n\nfunc worker(id int) {\n    fmt.Printf("Worker %d starting\\n", id)\n    time.Sleep(time.Second)\n    fmt.Printf("Worker %d done\\n", id)\n}\n\nfunc main() {\n    for i := 1; i <= 5; i++ {\n        go worker(i)\n    }\n    time.Sleep(2 * time.Second) // Solution de fortune : on attend la fin\n}
\n\n

Problème : dans cet exemple, nous utilisons time.Sleep pour attendre, ce qui n'est pas fiable. Au niveau avancé, on utilise les canaux et sync.WaitGroup.

\n\n

2. Synchronisation avec sync.WaitGroup

\n\n

sync.WaitGroup permet d'attendre la fin d'un nombre donné de goroutines. C'est un outil basique mais puissant pour la synchronisation.

\n\n
package main\n\nimport (\n    "fmt"\n    "sync"\n    "time"\n)\n\nfunc worker(id int, wg *sync.WaitGroup) {\n    defer wg.Done() // Décrémente le compteur à la fin\n    fmt.Printf("Worker %d starting\\n", id)\n    time.Sleep(time.Second)\n    fmt.Printf("Worker %d done\\n", id)\n}\n\nfunc main() {\n    var wg sync.WaitGroup\n    for i := 1; i <= 5; i++ {\n        wg.Add(1) // Incrémente le compteur avant le lancement\n        go worker(i, &wg)\n    }\n    wg.Wait() // Bloque jusqu'à ce que le compteur soit à zéro\n    fmt.Println("All workers completed")\n}
\n\n

Important : wg.Add() doit être appelé avant le lancement de la goroutine, sinon une condition de course est possible. Utilisez defer wg.Done() à l'intérieur de la goroutine pour garantir la décrémentation du compteur même en cas de panique.

\n\n

3. Canaux : bufférisation, direction et select

\n\n

Les canaux sont le mécanisme principal d'échange de données entre les goroutines. Au niveau avancé, on utilise les canaux bufférisés, les canaux unidirectionnels et l'opérateur select.

\n\n

3.1. Canaux bufférisés

\n

Les canaux bufférisés permettent d'envoyer des données sans réception immédiate. Cela réduit les blocages et augmente le débit.

\n
package main\n\nimport "fmt"\n\nfunc main() {\n    ch := make(chan int, 3) // canal avec un buffer de 3 éléments\n    ch <- 1\n    ch <- 2\n    ch <- 3\n    // ch <- 4 // blocage, car le buffer est plein\n    fmt.Println(<-ch)\n    fmt.Println(<-ch)\n    fmt.Println(<-ch)\n}
\n\n

3.2. Canaux unidirectionnels

\n

Pour la sécurité des types et pour indiquer explicitement le rôle d'un canal, on utilise les canaux unidirectionnels : chan<- (envoi uniquement) et <-chan (réception uniquement).

\n
package main\n\nimport "fmt"\n\nfunc sender(out chan<- int) {\n    for i := 0; i < 5; i++ {\n        out <- i\n    }\n    close(out)\n}\n\nfunc receiver(in <-chan int) {\n    for val := range in {\n        fmt.Println(val)\n    }\n}\n\nfunc main() {\n    ch := make(chan int)\n    go sender(ch)\n    receiver(ch)\n}
\n\n

3.3. Opérateur select

\n

select permet à une goroutine d'attendre plusieurs canaux simultanément. C'est

Blogs

Book Recommendations