← Назад к вопросам

Что делает метод Done?

1.3 Junior🔥 141 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Метод Done в контексте Go

Метод Done() — это ключевой элемент механизма отмены (cancellation) и сигнализации (signaling) в Go, тесно связанный с типами context.Context и sync.WaitGroup. Его основная функция — предоставить канал, который закрывается, когда должна произойти отмена операции или завершение ожидания.

Основное назначение и использование

Done() возвращает канал (<-chan struct{}), который:

  1. Изначально открыт (не готов к приёму данных)
  2. Закрывается при наступлении определённого события
  3. Используется для блокирующего ожидания в операторах select
// Типичное использование с context.Context
func worker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            // Контекст отменён, завершаем работу
            fmt.Println("Работа прервана")
            return
        default:
            // Выполняем полезную работу
            doWork()
        }
    }
}

Использование с context.Context

В пакете context метод Done() — один из основных методов интерфейса Context:

type Context interface {
    Done() <-chan struct{}
    Err() error
    Deadline() (deadline time.Time, ok bool)
    Value(key interface{}) interface{}
}

Ключевые моменты:

  • Когда контекст отменяется (через cancel(), таймаут или дедлайн), канал, возвращаемый Done(), закрывается
  • Чтение из закрытого канала немедленно возвращает нулевое значение
  • Это позволяет горутинам реагировать на запросы отмены без активного опроса
func processWithTimeout() {
    // Создаём контекст с таймаутом
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel() // Освобождаем ресурсы
    
    select {
    case <-ctx.Done():
        if ctx.Err() == context.DeadlineExceeded {
            fmt.Println("Превышено время ожидания")
        }
    case result := <-longRunningOperation():
        fmt.Println("Операция завершена:", result)
    }
}

Использование с sync.WaitGroup

Хотя sync.WaitGroup не имеет метода Done() в том же смысле, что и контекст, у него есть метод Done() для декрементации счётчика:

func processConcurrently() {
    var wg sync.WaitGroup
    
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done() // Уменьшаем счётчик при завершении
            fmt.Printf("Горутина %d работает\n", id)
        }(i)
    }
    
    wg.Wait() // Ждём, пока счётчик не станет 0
    fmt.Println("Все горутины завершены")
}

Паттерны использования

  1. Грейсфул шатдаун — корректное завершение работы при получении сигналов (SIGINT, SIGTERM):
func main() {
    ctx, cancel := context.WithCancel(context.Background())
    
    // Обработка сигналов ОС
    sigCh := make(chan os.Signal, 1)
    signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
    
    go func() {
        <-sigCh
        cancel() // Инициируем завершение
    }()
    
    runServer(ctx)
}
  1. Распространение отмены в иерархии горутин:
func parentProcess(ctx context.Context) {
    // Создаём дочерний контекст
    childCtx, cancel := context.WithCancel(ctx)
    defer cancel()
    
    // Передаём дочерний контекст в горутины
    go childProcess(childCtx)
    go anotherChildProcess(childCtx)
    
    // Отмена родительского контекста автоматически отменит дочерние
}
  1. Ограничение времени выполнения операций:
func fetchWithDeadline(url string) (string, error) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    
    req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    
    return io.ReadAll(resp.Body)
}

Важные особенности

  1. Канал предназначен только для чтения<-chan struct{} гарантирует, что только создатель контекста может инициировать отмену
  2. Закрытие вместо отправки данных — используется закрытие канала, а не отправка значений, что позволяет множеству горутин одновременно обнаружить отмену
  3. Идиоматический подход — проверка Done() в циклах является стандартным шаблоном в Go для обработки отмены
  4. Нулевые контекстыcontext.Background() и context.TODO() возвращают контексты, каналы Done() которых никогда не закрываются

Заключение

Метод Done() — это фундаментальный механизм для управления жизненным циклом горутин в Go. Он обеспечивает безопасный и эффективный способ координации параллельных операций, обработки таймаутов и реализации грациозного завершения приложений. Понимание работы Done() критически важно для написания корректных конкурентных программ на Go, которые правильно обрабатывают отмену операций и освобождают ресурсы.

Что делает метод Done? | PrepBro