Last active
July 24, 2026 15:14
-
-
Save skillissueru/0e4bbf95880dfc2299d0714b3ca17873 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| } | |
| } | |
| } |
Кмк в 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
}
А требуют на собесах еще Graceful shutdown воткнуть?
Т.е. после прихода сигнала ctx.Done() подождать некоторое время закрытия входящего канала in?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@irenkovich у автора так
case out <- val, у вас черезdefault: out <- val.На практике это значит, что вы проваливаетесь в default быстро, а внутри можете долго делать
out <- val, если читатель out занят прошлыми сообщениями. Надо как у автора через case, чтобы select выбирал то, что наступит быстрее: успешная запись в out, или отмена контекста