Go a été initialement conçu pour écriture de programmes réseau et parallèles à haute performance, donc une attention particulière a été portée au planificateur intégré (scheduler) et à la gestion simple du parallélisme à l'aide de goroutines. Contrairement à la plupart des langages, où les threads du système d'exploitation sont gérés directement par l'utilisateur du langage, dans Go, les goroutines sont plus légères, des milliers peuvent fonctionner simultanément sur un nombre fixe de threads système.
Problème : la gestion directe des threads est complexe : cela entraîne une rapide utilisation des ressources, des data races et des difficultés de gestion de la mémoire.
Solution : Go utilise un modèle M:N - un grand nombre de goroutines (M) sont multiplexées sur un nombre limité de threads système (N). C'est le planificateur, implémenté au niveau du runtime Go, qui assure l'équilibrage et le réallocation automatique de l'exécution des goroutines. Le programmeur gère uniquement le lancement et la synchronisation des goroutines, pas les threads du système d’exploitation directement.
Exemple de code :
package main import ( "fmt" "time" ) func worker(id int) { fmt.Printf("Worker %d starting\n", id) time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id) } func main() { for i := 0; i < 5; i++ { go worker(i) } time.Sleep(2 * time.Second) }
Caractéristiques clés :
Chaque goroutine s'exécutera-t-elle simultanément sur un cœur de processeur distinct ?
Non. Les goroutines sont multiplexées et le planificateur détermine le nombre réel de tâches exécutées en parallèle.
Peut-on gérer manuellement l'exécution de goroutines/threads spécifiques ?
Non. Le runtime Go ne fournit pas d'interface pour le planificateur direct. L'exception est GOMAXPROCS pour définir le nombre de threads système.
Un grand nombre de goroutines signifie-t-il une accélération automatique du programme ?
Non. Un grand nombre d'opérations concomitantes peut entraîner des surcharges supplémentaires : changements de contexte, contention pour les ressources, augmentation du temps GC et consommation de mémoire.
Un microservice traitait simultanément les requêtes entrantes, sans limiter le nombre de goroutines et sans attendre la fin via WaitGroup - résultat : temps de réponse accru, data races, délais d'attente imprévisibles.
Avantages :
Inconvénients :
Un gestionnaire de workers implémenté via un pool de goroutines, limitant le nombre de tâches en cours via un sémaphore ou des canaux. WaitGroup attend correctement la fin de toutes les tâches.
Avantages :
Inconvénients :