0Pricing
Go Academy · Lekcja

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

  1. Wzorzec puli workerów
  2. Fan-Out i Fan-In
  3. Etapy potoku
  4. Kontrolowane zamykanie
← Powrót do Go Academy