Een goroutine is een lichtgewicht uitvoeringsthread, beheerd door de Go-runtime. Om een nieuwe goroutine te starten, gebruik je het sleutelwoord go voor de functieaanroep. Onder de motorkap wordt er een structuur aangemaakt die de stack en de status van de taak beschrijft, die aan de wachtrij van de goroutinescheduler wordt toegevoegd.
In tegenstelling tot OS-threads heeft een goroutine een veel kleinere initiële stack (meestal 2 KB), en de stack wordt automatisch vergroot indien nodig. De Go-scheduler verdeelt ze automatisch over de beschikbare OS-threads (M:N-model).
Belangrijkste verschillen met threads:
package main import ( "fmt" "time" ) func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Printf("worker %d begon job %d\n", id, j) time.Sleep(time.Second) fmt.Printf("worker %d voltooide job %d\n", id, j) results <- j * 2 } } func main() { jobs := make(chan int, 5) results := make(chan int, 5) for w := 1; w <= 3; w++ { go worker(w, jobs, results) } for j := 1; j <= 5; j++ { jobs <- j } close(jobs) for a := 1; a <= 5; a++ { <-results } }
Kunnen Go-goroutines parallel draaien op meerdere cores?
Een vaak voorkomende foutieve antwoord is: "Nee, omdat Go groene threads gebruikt." In werkelijkheid kan Go, met behulp van een omgevingsvariabele of de oproep runtime.GOMAXPROCS(n), de uitvoering van goroutines op alle beschikbare procescores paralleliseren.
import "runtime" func main() { runtime.GOMAXPROCS(4) // Sta toe om 4 cores te gebruiken ... }
Verhaal
In een backend-serviceproject in Go was er een pool van workers geïmplementeerd via goroutines, maar de programmeurs vergeten het aantal gelijktijdig draaiende goroutines te beperken. Als gevolg hiervan startte de applicatie duizenden goroutines bij toenemende belasting, wat leidde tot geheugenuitputting en crash van de service. Het probleem werd opgelost door een limiet in te voeren voor actieve goroutines (bijvoorbeeld met behulp van een semaphore of worker pool).
Verhaal
Een van de medewerkers synchroniseerde gegevens tussen goroutines verkeerd door gewone globale variabelen te gebruiken zonder mutexen of kanalen. Dit veroorzaakte een race condition, waardoor er af en toe fouten optraden bij het verwerken van betalingen. Het probleem werd pas ontdekt nadat het in productie was.
Verhaal
In de parsingservice was het moment van het doorgeven van nil-kanalen in select gemist: na sluiting van het kanaal bleef select geblokkeerd wachten op gegevens, waardoor sommige goroutines "vastliepen". Dit werd verholpen door nil toe te wijzen aan het gesloten kanaal en select correct te verwerken.