Верните самый быстрый из N поисковиков, запущенных конкурентно
Реализуйте getFastestSearcher: запустите testSearcher(ctx, name) конкурентно для каждого имени из searches и верните имя, чей вызов сообщил наименьшую time.Duration, вместе с этой длительностью. Требования: запускайте пробы конкурентно, а не по одной; пробрасывайте ctx, чтобы отмена останавливала пробы в полёте; если все пробы упали, верните последнюю ошибку; не допускайте утечки горутин.
func getFastestSearcher(ctx context.Context, searches []string) (name string, respTime time.Duration, err error) {
// ваш код здесь
}
Допишите реализацию.
Запускают по одной горутине на имя, каждая измеряет testSearcher(ctx, name) и шлёт результат {name, dur, err} в буферизованный канал, который закрывающая горутина close-ит после WaitGroup. Проходят range по каналу, держат результат с наименьшим dur среди успешных и пробрасывают ctx, чтобы отмена останавливала пробы в полёте. Если все упали — возвращают последнюю ошибку.
- ✗Возвращать первый ответивший поисковик, а не тот, у кого наименьшая сообщённая длительность
- ✗Обновлять общие
name/respTimeиз горутин без синхронизации — это data race - ✗Закрывать канал результатов из отправителя или не закрывать вовсе, из-за чего
rangeзависает навсегда
- →Как вернуться раньше, как только поисковик обгонит целевую задержку, отменив остальных?
- →Почему канал должен быть буферизован (или отправки защищены), чтобы не течь горутинами при раннем выходе?
Скелет для реализации
Канал результатов буферизуют по числу проб, чтобы отправители не зависли, и закрывают отдельной горутиной после wg.Wait. По каналу проходят range, удерживая пробу с наименьшим dur среди успешных; если успешных нет — возвращают последнюю ошибку. ctx пробрасывают в каждый testSearcher, а отправку защищают select с ctx.Done(), чтобы не течь горутинами при раннем выходе.
type res struct {
name string
dur time.Duration
err error
}
func getFastestSearcher(ctx context.Context, searches []string) (name string, respTime time.Duration, err error) {
out := make(chan res, len(searches)) // буфер по числу проб — отправители не зависнут
var wg sync.WaitGroup
for _, s := range searches {
wg.Add(1)
go func(s string) {
defer wg.Done()
d, e := testSearcher(ctx, s)
select {
case out <- res{s, d, e}:
case <-ctx.Done(): // не блокируемся, если уходим раньше
}
}(s)
}
go func() { wg.Wait(); close(out) }() // close только после всех Done
var best res
found := false
for r := range out {
if r.err != nil {
err = r.err // запоминаем последнюю ошибку
continue
}
if !found || r.dur < best.dur {
best, found = r, true
}
}
if found {
return best.name, best.dur, nil
}
return "", 0, err
}