Przejdź do głównej treści
eLearner.app
Moduł 7 · Lekcja 1 z 531/50 w kursie~12 min
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
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

Go
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:

Go
// 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:

Go
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:

Go
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

AspektGorutynaWątek systemu operacyjnego
Początkowy stos~2KB1–8 MB (przydzielane na początku)
Stworzenienanosekundymikrosekundy
HarmonogramPrzejdź do środowiska uruchomieniowego (współpraca)jądro (wywłaszczające)
Praktyczne liczenie100 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

Ćwiczenie#go.m7.l1.e1
Próby: 0Ładowanie...

Uruchom funkcję greet jako goroutine i odczekaj 100 ms z czasem. Uśpij się przed wyjściem z głównego.

Ładowanie edytora...
Pokaż wskazówkę

`go` przed wywołaniem funkcji uruchamia goroutine.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie#go.m7.l1.e2
Próby: 0Ładowanie...

Uruchom anonimową funkcję jako goroutine, która wypisuje „cześć”.

Ładowanie edytora...
Pokaż wskazówkę

Funkcja anonimowa jest wywoływana z końcowym kodem `()`: `go func(){...}()`.

Rozwiązanie dostępne po 3 próbach

Quiz#go.m7.l1.e3
Gotowe

Co się stanie, jeśli `main` zakończy działanie przed zakończeniem goroutine?

Go
func main() {
  go work()
  // main ritorna immediatamente
}
Opcje odpowiedzi

Podsumowanie

  • go f() uruchamia jednocześnie f(); wraca natychmiast.
  • Goroutine = początkowe ~2 KB, zmultipleksowane na kilka wątków systemu operacyjnego.
  • main nie czeka: potrzebujesz synchronizacji (kanał/grupa oczekujących/kontekst).
  • time.Sleep ma charakter wyłącznie dydaktyczny, nigdy nie służy do synchronizacji w produkcji.
  • Od wersji 1.22 zmienna for jest „świeża” w każdej iteracji (koniec z błędem „3 3 3”).