package main
import (
"fmt"
"time"
)
// worker - это наша горутина, которая выполняет "работу"
// Она будет работать до тех пор, пока не получит сигнал об остановке
// или не истечет время таймаута.
func worker(stopCh <-chan struct{}) {
fmt.Println("Воркер начал работу...")
// Используем select для ожидания нескольких событий
select {
// Case 1: Если в канал stopCh что-то пришло (сигнал об остановке)...
case <-stopCh:
fmt.Println("Воркер получил сигнал об остановке и завершил работу.")
// Case 2: Если прошло 2 секунды (сработал таймаут)...
case <-time.After(2 * time.Second):
fmt.Println("Воркер не завершил работу за 2 секунды и остановился по таймауту.")
}
}
func main() {
// --- Сценарий 1: Остановка по таймауту ---
fmt.Println("--- Сценарий 1: Таймаут ---")
stopCh1 := make(chan struct{})
go worker(stopCh1)
// Ждем 3 секунды, чтобы гарантированно сработал таймаут в 2 секунды
time.Sleep(3 * time.Second)
// --- Сценарий 2: Остановка по сигналу ---
fmt.Println("\n--- Сценарий 2: Внешний сигнал ---")
stopCh2 := make(chan struct{})
go worker(stopCh2)
// Ждем 1 секунду и отправляем сигнал об остановке
time.Sleep(1 * time.Second)
fmt.Println("Main отправляет сигнал об остановке...")
close(stopCh2) // Закрытие канала - это тоже сигнал, который получат все слушающие
// Даем воркеру время на завершение
time.Sleep(500 * time.Millisecond)
fmt.Println("Программа завершена.")
} package main
import (
"fmt"
"time"
)
func main() {
messages := make(chan string)
// Запускаем горутину, которая будет получать сообщение через 2 секунды
go func() {
time.Sleep(2 * time.Second)
msg := <-messages // Получаем сообщение, разблокируя main
fmt.Println("Получатель получил:", msg)
}()
// Попытка отправить сообщение без ожидания
select {
case messages <- "привет!":
fmt.Println("Сообщение отправлено.")
default:
fmt.Println("Никто не готов принять сообщение. Отправка отменена.")
}
// Подождем немного, чтобы получатель успел сработать
time.Sleep(3 * time.Second)
fmt.Println("Программа завершена.")
} package main
import (
"fmt"
"time"
)
// worker будет работать, пока не получит сигнал по каналу stopCh
func worker(id int, stopCh <-chan struct{}) {
fmt.Printf("Воркер #%d запущен\n", id)
for {
select {
// Case 1: Пришел сигнал об остановке
case <-stopCh:
fmt.Printf("Воркер #%d получил сигнал СТОП и завершает работу\n", id)
return // Выходим из функции, горутина завершается
// Case 2: Сигнала нет, продолжаем работать
default:
fmt.Printf("Воркер #%d работает...\n", id)
time.Sleep(500 * time.Millisecond)
}
}
}
func main() {
stopCh := make(chan struct{}) // Канал для сигнала
// Запускаем воркера
go worker(1, stopCh)
// Позволим ему поработать 2 секунды
fmt.Println("Main дает воркеру поработать 2 секунды...")
time.Sleep(2 * time.Second)
// Отправляем сигнал об остановке. Закрытие канала — это и есть сигнал.
// Все горутины, которые слушают <-stopCh, получат этот сигнал.
fmt.Println("Main отправляет сигнал СТОП.")
close(stopCh)
// Даем время воркеру на корректное завершение
time.Sleep(500 * time.Millisecond)
fmt.Println("Программа завершена.")
} package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, jobs <-chan int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Воркер #%d запущен\n", id)
// Этот цикл автоматически завершится, когда канал jobs будет закрыт
for j := range jobs {
fmt.Printf("Воркер #%d выполняет задание %d\n", id, j)
time.Sleep(time.Second)
}
fmt.Printf("Воркер #%d: канал заданий закрыт, работа завершена.\n", id)
}
func main() {
jobs := make(chan int)
var wg sync.WaitGroup
// Запускаем 3 воркеров
for w := 1; w <= 3; w++ {
wg.Add(1)
go worker(w, jobs, &wg)
}
// Отправляем 5 заданий
for j := 1; j <= 5; j++ {
jobs <- j
}
// КЛЮЧЕВОЙ МОМЕНТ: Закрываем канал заданий.
// Это сигнал для ВСЕХ воркеров, что новых заданий больше не будет,
// и они могут завершиться, как только закончат текущую работу.
close(jobs)
fmt.Println("Все задания отправлены, канал закрыт. Воркеры завершают текущую работу и останавливаются.")
// Ждем, пока все воркеры завершат свою работу
wg.Wait()
fmt.Println("Все воркеры завершили работу. Программа завершена.")
} package main
func main() {
// Создаем канал, но никто никогда не будет отправлять в него данные
ch := make(chan int)
// Запускаем горутину, которая будет вечно ждать
go func() {
// Эта строка заблокирует горутину навсегда
fmt.Println("Горутина ждет данные...")
<-ch
fmt.Println("Горутина получила данные.") // ЭТОТ КОД НИКОГДА НЕ ВЫПОЛНИТСЯ
}()
// main завершает работу, но горутина остается "зависшей" в памяти.
// Это и есть утечка.
fmt.Println("Main завершается.")
} package main
import "time"
func leakyProducer(ch chan<- string) {
// Эта горутина пытается отправить данные и заблокируется,
// пока кто-то не будет готов их принять.
ch <- "результат"
fmt.Println("Данные отправлены") // ЭТОТ КОД НИКОГДА НЕ ВЫПОЛНИТСЯ
}
func main() {
ch := make(chan string)
go leakyProducer(ch)
// main что-то делает и завершается, не забрав данные из канала.
// Горутина leakyProducer навсегда останется заблокированной на операции ch <- "результат".
time.Sleep(100 * time.Millisecond)
fmt.Println("Main завершается, не прочитав результат.")
// Это утечка.
} func handleRequest(ctx context.Context) { ... }
func fetchFromDB(ctx context.Context, query string) { ... } package main
import (
"context"
"fmt"
"time"
)
// doWork выполняет "долгую" работу, но уважает контекст
func doWork(ctx context.Context) {
fmt.Println("Воркер начал работу...")
for i := 0; i < 5; i++ {
select {
// Case 1: Контекст был отменен (по таймауту или вручную)
case <-ctx.Done():
// ctx.Err() расскажет причину отмены
fmt.Printf("Работа отменена. Причина: %v\n", ctx.Err())
return
// Case 2: Все в порядке, продолжаем работать
default:
fmt.Printf("Работаю... шаг %d\n", i+1)
time.Sleep(1 * time.Second)
}
}
fmt.Println("Работа успешно завершена.")
}
func main() {
// Сценарий 1: Отмена по таймауту
fmt.Println("--- Сценарий 1: Таймаут 2 секунды ---")
// Создаем контекст, который отменится через 2 секунды
ctxTimeout, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() // Важно! Вызывать defer cancel() для освобождения ресурсов
doWork(ctxTimeout)
// Сценарий 2: Ручная отмена
fmt.Println("\n--- Сценарий 2: Ручная отмена через 1 секунду ---")
// Создаем контекст, который можно отменить вручную
ctxCancel, cancel := context.WithCancel(context.Background())
go doWork(ctxCancel) // Запускаем в горутине
// Ждем 1 секунду и отменяем
time.Sleep(1 * time.Second)
fmt.Println("Main решает отменить работу...")
cancel() // Отправляем сигнал отмены
// Даем время воркеру на завершение
time.Sleep(500 * time.Millisecond)
} func myHandler(w http.ResponseWriter, r *http.Request) {
// Берем контекст из запроса
ctx := r.Context()
// Передаем его в функцию работы с БД
data, err := db.Query(ctx, "SELECT * FROM products")
// Если клиент закроет соединение, запрос к БД будет отменен.
// ...
}