Kontrolowane zamykanie
Poprawnie zatrzymuj workery
Kontrolowane zamykanie to bezpłatna lekcja Go Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Go Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Go Academy zawiera 4 lekcji w sumie.
Dlaczego kontrolowane zamykanie
Działające przez długi czas goroutines muszą otrzymać informację, kiedy mają się zatrzymać. Kontrolowane zamykanie pozwala workerom dokończyć bieżącą pracę, zwolnić zasoby i zakończyć działanie bez wycieków ani utraty danych.
- Brak wycieków goroutines
- Brak częściowo zakończonych zapisów
- Poprawne zakończenie procesu
Wzorzec kanału done
Klasycznym rozwiązaniem jest kanał done. Jego zamknięcie rozsyła sygnał zatrzymania do wszystkich goroutines, które go nasłuchują, ponieważ odczyt z zamkniętego kanału zwraca natychmiast.
done := make(chan struct{})
// ... later
close(done)select dla sygnałów zatrzymania
Worker używa select, aby albo przetworzyć zadanie, albo wykryć zamknięcie kanału done. Gdy kanał done zostanie zamknięty, odpowiednia gałąź zostaje wykonana, a worker kończy działanie.
for {
select {
case <-done:
return
case j := <-jobs:
process(j)
}
}context.Context
Współczesnym, idiomatycznym narzędziem jest context. context.WithCancel udostępnia context oraz funkcję cancel. Wywołanie cancel zamyka kanał Done danego contextu.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()Nasłuchiwanie na ctx.Done()
Workery wykonują select na ctx.Done(). Po wywołaniu cancel lub upływie terminu kanał zostaje zamknięty, a worker kończy działanie.
select {
case <-ctx.Done():
return ctx.Err()
case j := <-jobs:
process(j)
}Oczekiwanie na workery
Samo wysłanie sygnału zatrzymania nie wystarczy — należy poczekać, aż workery rzeczywiście zakończą pracę. Obiekt sync.WaitGroup śledzi ich działanie, dzięki czemu main może się blokować, dopóki licznik nie osiągnie zera.
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
runWorker(ctx)
}()
// ... later
cancel()
wg.Wait()Limity czasu z context
context.WithTimeout automatycznie anuluje context po określonym czasie. Ogranicza to czas, przez jaki może trwać zamykanie, zanim konieczne będzie wymuszenie zakończenia.
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()Kompletne kontrolowane zamykanie
Ten program uruchamia dwóch workerów, anuluje context i czeka, aż oba poprawnie zakończą działanie, zanim wypisze done.
package main
import (
"context"
"fmt"
"sync"
"time"
)
func worker(ctx context.Context, id int, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case <-ctx.Done():
fmt.Printf("worker %d stopping\n", id)
return
case <-time.After(50 * time.Millisecond):
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
for i := 1; i <= 2; i++ {
wg.Add(1)
go worker(ctx, i, &wg)
}
time.Sleep(120 * time.Millisecond)
cancel()
wg.Wait()
fmt.Println("all workers stopped")
}Opróżnianie zadań w toku
Podczas zamykania często chcemy, aby workery dokończyły już rozpoczętą pracę. Można odczytać pozostałe zadania w końcowej pętli albo przestać przyjmować nowe zadania i pozwolić na dokończenie bieżących.
Przechwytywanie sygnałów systemu operacyjnego
W rzeczywistych serwerach signal.NotifyContext wiąże anulowanie z sygnałami SIGINT i SIGTERM. Naciśnięcie Ctrl+C uruchamia wtedy tę samą ścieżkę kontrolowanego zamykania co ręczne anulowanie.
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()Nigdy nie zapominaj o cancel()
Zawsze należy używać defer cancel(), także w przypadku WithTimeout. Brak wywołania cancel powoduje wyciek wewnętrznej goroutine i timera contextu aż do upływu terminu.
Szybki test
Proszę sprawdzić zrozumienie kontrolowanego zamykania.
Podsumowanie
Poznali Państwo kontrolowane zamykanie:
- Do sygnalizowania zatrzymania należy używać context.WithCancel lub kanału done
- Workery wykonują select na ctx.Done()
- WaitGroup gwarantuje, że wszystkie workery rzeczywiście zakończą działanie
- W rzeczywistych serwerach anulowanie należy powiązać z sygnałami systemu operacyjnego
- Zawsze należy używać defer cancel()
Często zadawane pytania
Czy lekcja „Kontrolowane zamykanie” jest bezpłatna?
Tak — pełny tekst „Kontrolowane zamykanie” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Go Academy, przejdź na CoddyKit PRO. Kurs Go Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Kontrolowane zamykanie”?
Poprawnie zatrzymuj workery Ćwiczysz Go Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Go Academy?
Nie wymagamy żadnego doświadczenia. Go Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Kontrolowane zamykanie”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Go Academy?
Tak. Każda lekcja Go Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Wzorzec puli workerów
- Fan-Out i Fan-In
- Etapy potoku
- Kontrolowane zamykanie