Использование гороутин

Подскажите пожалуйста, как реализовать грамотно такой алгоритм, используя функицонал Go:

  1. запускаются несколько гороутин, которые возвращают результат, каждая - в произвольный момент времени.

  2. если таймаут - завершаем все работающие горутины

  3. если какая-либо гороутина вернула результат, то

    3а) если результат положительный, то завершаем все остальные работающие горутины

    3б) иначе - ждем результата от следующей гороутины

     answers := make(chan UpstreamAnswer, len(upstream_list))
    
     var result int = 0
     // запускаем гороутины
     for _, dl := range upstream_list {
         go test_conn(cx, dl.idx, proto, host_req, port_req, answers)
     }
    
     // ждем ответа(ов)
     for i := 0; i < 100; i++ {
         select {
         case dialer = <-answers:
                // проверяем ответ, если ОК - выходим из цикла
                if dialer.OK {
                      result = dialer.idx
                }
         case <-time.After(time.Millisecond * 100):
                // тут еще какая-то работа идет периодически
         }
         if result != 0 {
                break
         }
     }
     // завершаем гороутины через контекст
     cancel()
     close(answers)
     // здесь проверяем уже результат
     if result ...
    

Ответы (1 шт):

Автор решения: Borislav
type Result struct {
    isPositive bool
}

var i int

func doSomeWorkForProvideResult() Result {
    defer func() {
        i++
    }()
    if i > 100 {
        return Result{true}
    }
    return Result{}
}

func main() {
    ch := make(chan Result)

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

    const timeout = time.Second * 10
    timer := time.NewTimer(timeout)
    defer timer.Stop()

    for w := 0; w < runtime.NumCPU(); w++ {
        go func() {
            for {
                select {
                case <-ctx.Done():
                    return
                default:
                    if res := doSomeWorkForProvideResult(); res.isPositive {
                        ch <- res
                        cancel()
                        return
                    }
                }
            }
        }()
    }

    go func() {
        for {
            select {
            case <-ctx.Done():
                return
            case <-timer.C:
                cancel()
                return
            }
        }
    }()

    res := <-ch
    fmt.Printf("found positive result: %+v\n", res)
}
→ Ссылка