Lekcje modułu (1/5)
Goroutines: lekka współbieżność
goroutine to jednostka współbieżnego wykonywania zarządzana przez Go
czas wykonania. Uruchamiasz go ze słowem kluczowym go przed funkcją
zadzwoń:
go greet("Anna") // chiamata a funzione esistente
go func() { // funzione anonima
fmt.Println("hi")
}()Instrukcja go zwraca natychmiast: rozpoczyna się goroutine
równolegle, a osoba dzwoniąca kontynuuje pracę.
Dlaczego są „lekkie”
- Początkowy stos ~2KB (rośnie dynamicznie, w razie potrzeby do setek MB).
- Multipleksowanie na niewielką liczbę wątków systemu operacyjnego w środowisku wykonawczym (
GOMAXPROCS). - Tysiące/miliony gorutyn to rutyna; z wątkami systemu operacyjnego byłoby to niemożliwe.
Konceptualnie jest to „równoczesne odpal i zapomnij”, ale ze wszystkimi
idiomatyczne narzędzia do koordynacji (kanały, sync.WaitGroup,
KODEF1).
główny nie czeka
func main() {
go func() { fmt.Println("ciao dalla goroutine") }()
// main esce subito: niente garanzia che la goroutine completi
}Kiedy main powraca, proces kończy się: goroutines nie są wykonywane
aby „dokończyć swoją pracę”, są po prostu zabijani razem z
proces. Wymagana jest jawna synchronizacja.
Synchronizacja: podgląd
Trzy narzędzia, które zobaczysz w następnych lekcjach:
// 1) Channel: la goroutine "segnala" via canale
done := make(chan struct{})
go func() {
work()
done <- struct{}{}
}()
<-done // attendi
// 2) sync.WaitGroup: N goroutine
var wg sync.WaitGroup
wg.Add(1)
go func() { defer wg.Done(); work() }()
wg.Wait()
// 3) context: cancellazione/timeout propagati„Klasyczny” błąd związany z zamknięciem pętli
Do wersji Go 1.21 był to częsty problem:
for i := 0; i < 3; i++ {
go func() { fmt.Println(i) }() // stampa "3 3 3" su versioni vecchie
}Od Go 1.22 zmienna for jest świeżo tworzona na każdym
iterację, więc błąd zostanie domyślnie naprawiony. W starszych bazach kodu tak będzie
nadal widzę wzór naprawy:
for i := 0; i < 3; i++ {
i := i // shadow esplicito
go func() { fmt.Println(i) }()
}
// oppure passa come argomento:
for i := 0; i < 3; i++ {
go func(n int) { fmt.Println(n) }(i)
}Goroutine != wątek systemu operacyjnego
| Aspekt | Gorutyna | Wątek systemu operacyjnego |
|---|---|---|
| Początkowy stos | ~2KB | 1–8 MB (przydzielane na początku) |
| Stworzenie | nanosekundy | mikrosekundy |
| Harmonogram | Przejdź do środowiska uruchomieniowego (współpraca) | jądro (wywłaszczające) |
| Praktyczne liczenie | 100 tys.+ | kilka tysięcy |
Pojedynczy wątek systemu operacyjnego może obsługiwać wiele procedur Goroutine. Środowisko wykonawcze się porusza w razie potrzeby goroutines między wątkami.
Spróbuj
Uruchom funkcję greet jako goroutine i odczekaj 100 ms z czasem. Uśpij się przed wyjściem z głównego.
Pokaż wskazówkę
`go` przed wywołaniem funkcji uruchamia goroutine.
Rozwiązanie dostępne po 3 próbach
Uruchom anonimową funkcję jako goroutine, która wypisuje „cześć”.
Pokaż wskazówkę
Funkcja anonimowa jest wywoływana z końcowym kodem `()`: `go func(){...}()`.
Rozwiązanie dostępne po 3 próbach
Co się stanie, jeśli `main` zakończy działanie przed zakończeniem goroutine?
func main() {
go work()
// main ritorna immediatamente
}Podsumowanie
go f()uruchamia jednocześnie f(); wraca natychmiast.- Goroutine = początkowe ~2 KB, zmultipleksowane na kilka wątków systemu operacyjnego.
mainnie czeka: potrzebujesz synchronizacji (kanał/grupa oczekujących/kontekst).time.Sleepma charakter wyłącznie dydaktyczny, nigdy nie służy do synchronizacji w produkcji.- Od wersji 1.22 zmienna
forjest „świeża” w każdej iteracji (koniec z błędem „3 3 3”).