Skip to content

Instantly share code, notes, and snippets.

@skillissueru
Last active July 24, 2026 15:14
Show Gist options
  • Select an option

  • Save skillissueru/0e4bbf95880dfc2299d0714b3ca17873 to your computer and use it in GitHub Desktop.

Select an option

Save skillissueru/0e4bbf95880dfc2299d0714b3ca17873 to your computer and use it in GitHub Desktop.
package main
import (
"context"
"errors"
"fmt"
"math/rand"
"sync"
"time"
)
// Более оптимальное решение задачи из этого видео:
// https://youtu.be/wZCLVt_5-4c
// Из сигнатуры функции processData убран кастомный тип + убран один селект
// (он не был прям сильно лишним, но от него можно избавиться +- безболезненно)
// условие задачи:
// реализовать функцию processParallel
// прокинуть контекст
var errTimeout = errors.New("timed out")
func processData(ctx context.Context, v int) (int, error) {
ch := make(chan struct{})
go func() {
time.Sleep(time.Duration(rand.Intn(10)) * time.Second)
close(ch)
}()
select {
case <-ch:
case <-ctx.Done():
return 0, errTimeout
}
return v * 2, nil
}
func main() {
in := make(chan int)
out := make(chan int)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
go func() {
defer close(in)
for i := range 10 {
select {
case in <- i + 1:
case <-ctx.Done():
return
}
}
}()
start := time.Now()
processParallel(ctx, in, out, 5)
for v := range out {
fmt.Println("v =", v)
}
fmt.Println("main duration:", time.Since(start))
}
func processParallel(ctx context.Context, in <-chan int, out chan<- int, numWorkers int) {
wg := &sync.WaitGroup{}
for range numWorkers {
wg.Add(1)
go worker(ctx, in, out, wg)
}
go func() {
wg.Wait()
close(out)
}()
}
func worker(ctx context.Context, in <-chan int, out chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case v, ok := <-in:
if !ok {
return
}
val, err := processData(ctx, v)
if errors.Is(err, errTimeout) {
return
}
//...handle other error types
select {
case <-ctx.Done():
return
case out <- val:
}
case <-ctx.Done():
return
}
}
}
@IlVin

IlVin commented Jul 14, 2026

Copy link
Copy Markdown

Кмк в processData не надо городить каналы и запускать тяжелую функцию в отдельной горутине (паттерн "генератор").
Потому, что такая стратегия ведет к утечке горутин (представьте, что происходит частый старт/стоп воркер пула, тогда успевшие стартовать тяжелые процессы забьют всю память).
Думаю, что нужно этим "2м чувакам" сказать, что можно запустить процесс в фоновой горутине, но раз уж мы пробросили контекст в processData (изменили сигнатуру функции), то пробрасываем его до конца:

func processData (ctx context.Context, v int) (int, error) {
	select {
	case <-ctx.Done():
		return 0, errors.New("cancel")
	case <-time.After(time.Duration(rand.Intn(10))*time.Second):
	}
	return v * 2, nil
}

@IlVin

IlVin commented Jul 14, 2026

Copy link
Copy Markdown

А требуют на собесах еще Graceful shutdown воткнуть?
Т.е. после прихода сигнала ctx.Done() подождать некоторое время закрытия входящего канала in?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment