Используйте фиксированное число работников, общий context и mutex для карты. Для остановки активной загрузки сама функция скачивания должна принимать context.
Одновременное получение данных с нескольких веб‑ресурсов
Используйте фиксированное число работников, общий context и mutex для карты. Для остановки активной загрузки сама функция скачивания должна принимать context.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Необходимо написать функцию, которая будет загружать содержимое нескольких веб‑страниц параллельно, используя фиксированный пул воркеров.
Требования:
- Создать workersCount воркеров (горутины).
- Каждый воркер читает URL из канала inputUrls и скачивает страницу через функцию DownloadSiteContent.
- Полученные результаты помещаются в общую потокобезопасную карту.
- При получении сигнала отмены из контекста все воркеры должны корректно завершить работу.
Ответ
Используйте фиксированное число работников, общий context и mutex для карты. Для остановки активной загрузки сама функция скачивания должна принимать context.
Контракт DownloadSiteContent не задан. Ниже передаём её как download(ctx, url) (string, error); входной канал закрывает производитель. Возвращаются частичные данные и первая ошибка.
import (
"context"
"errors"
"sync"
)
func DownloadAll(ctx context.Context, workersCount int,
inputUrls <-chan string,
download func(context.Context, string) (string, error),
) (map[string]string, error) {
if workersCount < 1 { return nil, errors.New("workersCount must be positive") }
ctx, cancel := context.WithCancel(ctx)
defer cancel()
results := make(map[string]string)
var mu sync.Mutex
var wg sync.WaitGroup
failures := make(chan error, 1)
for i := 0; i < workersCount; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
if ctx.Err() != nil { return }
select {
case <-ctx.Done():
return
case url, ok := <-inputUrls:
if !ok { return }
if ctx.Err() != nil { return }
body, err := download(ctx, url)
if err != nil {
select { case failures <- err: default: }
cancel()
return
}
mu.Lock()
results[url] = body
mu.Unlock()
}
}
}()
}
wg.Wait()
select {
case err := <-failures:
return results, err
default:
return results, ctx.Err()
}
}
Повторные URL перезаписываются последним завершившимся результатом. Производитель тоже должен учитывать ctx, чтобы не зависнуть на отправке. Если download игнорирует отмену, внешний пул не может безопасно прервать её выполнение.