0Pricing
Go Academy · Урок

Корректное завершение

Корректно останавливайте работников

«Корректное завершение» — бесплатный урок Go Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Go Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Go Academy содержит 4 уроков всего.

Зачем нужно корректное завершение

Долгоживущим горутинам нужно сообщить, когда остановиться. Корректное завершение позволяет worker закончить текущую работу, освободить ресурсы и завершиться без утечек или потери данных.

  • Нет утечек горутин
  • Нет незавершённых записей
  • Процесс завершается чисто

Шаблон канала done

Классический подход — канал done. Его закрытие рассылает сигнал остановки всем горутинам, которые его слушают, поскольку чтение из закрытого канала завершается немедленно.

done := make(chan struct{})
// ... later
close(done)

select для сигналов Stop

Worker использует select, чтобы либо обработать задачу, либо заметить канал done. Когда done закрывается, срабатывает соответствующая ветвь, и worker возвращается.

for {
    select {
    case <-done:
        return
    case j := <-jobs:
        process(j)
    }
}

Контекст context.Context

Современный идиоматичный инструмент — context. context.WithCancel предоставляет контекст и функцию отмены. Вызов cancel закрывает канал Done контекста.

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

Ожидание на ctx.Done()

Worker выполняют выбор в ctx.Done(). Когда вызывается cancel или истекает крайний срок, канал закрывается и worker завершается.

select {
case <-ctx.Done():
    return ctx.Err()
case j := <-jobs:
    process(j)
}

Ожидание завершения worker

Одного сигнала остановки недостаточно: необходимо дождаться фактического завершения worker. Объект sync.WaitGroup отслеживает их, чтобы main мог заблокироваться до обнуления счётчика.

var wg sync.WaitGroup
wg.Add(1)
go func() {
    defer wg.Done()
    runWorker(ctx)
}()
// ... later
cancel()
wg.Wait()

Тайм-ауты с помощью context

context.WithTimeout автоматически отменяет контекст по истечении заданного времени. Это ограничивает продолжительность завершения, прежде чем потребуется принудительно завершить процесс.

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

Полное корректное завершение

Эта программа запускает два worker, отменяет контекст и ждёт чистого завершения обоих, прежде чем напечатать 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")
}

Завершение текущей работы

При завершении часто требуется, чтобы worker закончили уже начатую работу. Прочитайте оставшиеся задачи в последнем цикле или перестаньте принимать новые задачи, позволив текущим завершиться.

Перехват сигналов OS

В настоящих серверах signal.NotifyContext связывает отмену с SIGINT и SIGTERM. После нажатия Ctrl+C запускается тот же путь корректного завершения, что и при ручной отмене.

ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()

Не забывайте cancel()

Всегда используйте defer cancel(), даже при использовании WithTimeout. Если не вызвать cancel, внутренняя горутина и таймер контекста будут оставаться активными до истечения крайнего срока.

Быстрая проверка

Проверьте, насколько хорошо Вы понимаете корректное завершение.

Итоги

Вы изучили корректное завершение:

  • Используйте context.WithCancel или канал done для сигнала остановки
  • Worker выполняют выбор на ctx.Done()
  • WaitGroup гарантирует фактическое завершение всех worker
  • Связывайте отмену с сигналами OS в настоящих серверах
  • Всегда используйте defer cancel()

Часто задаваемые вопросы

Урок «Корректное завершение» бесплатный?

Да — полный текст урока «Корректное завершение» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Go Academy, подпишись на CoddyKit PRO. Курс Go Academy содержит 4 уроков всего.

Чему я научусь в уроке «Корректное завершение»?

Корректно останавливайте работников Ты практикуешь Go Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Go Academy?

Предыдущий опыт не требуется. Go Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Корректное завершение»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Go Academy?

Да. Каждый урок Go Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Паттерн пула работников
  2. Fan-Out и Fan-In
  3. Этапы конвейера
  4. Корректное завершение
← Назад к Go Academy