Создайте numWorkers читателей канала и защищайте результирующую map. При отмене прекращайте брать новые URL, но уже начавшаяся функция из условия не остановится: она игнорирует context.
Одновременное скачивание контента с множества веб‑ресурсов
Создайте numWorkers читателей канала и защищайте результирующую map. При отмене прекращайте брать новые URL, но уже начавшаяся функция из условия не остановится: она игнорирует context.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Требуется написать функцию ParallelDownload, которая будет обрабатывать поступающие URL‑адреса параллельно, используя заданное количество воркеров, и возвращать отображение (map) с результатами загрузки контента для каждого сайта.
package main
import (
"context"
"fmt"
"math/rand"
"time"
)
type SiteContent struct {
Title string
Date time.Time
Content string
}
// Функция для имитации долгой работы, загрузки контента сайта
func DownloadSiteContent(ctx context.Context, url string) SiteContent {
// Генерация случайного времени ожидания от 5 до 10 секунд
rand.Seed(time.Now().UnixNano())
sleepTime := time.Duration(rand.Intn(6)+5) * time.Second
// Ожидание случайного времени
time.Sleep(sleepTime)
// Возвращаем имитацию контента сайта
return SiteContent{
Title: "Заголовок сайта " + url,
Date: time.Now(),
Content: "Содержание сайта " + url,
}
}
// Функция для параллельного скачивания контента сайтов
func ParallelDownload(ctx context.Context, urls <-chan string, numWorkers int) map[string]SiteContent {
// TODO
return nil
}
func main() {
urls := make(chan string)
// Запускаем функцию для параллельного скачивания контента
go func() {
urls <- "http://example.com"
urls <- "http://example.org"
urls <- "http://example.net"
close(urls)
}()
// Запускаем параллельное скачивание с максимальным количеством воркеров
result := ParallelDownload(context.Background(), urls, 3)
// Выводим результаты
for url, content := range result {
fmt.Printf("Сайт: %s\nЗаголовок: %s\nДата: %s\nСодержание: %s\n\n",
url, content.Title, content.Date, content.Content)
}
}
Ответ
Создайте numWorkers читателей канала и защищайте результирующую map. При отмене прекращайте брать новые URL, но уже начавшаяся функция из условия не остановится: она игнорирует context.
Добавьте sync к импортам:
func ParallelDownload(ctx context.Context, urls <-chan string, numWorkers int) map[string]SiteContent {
if numWorkers < 1 { panic("workers must be positive") }
result := make(map[string]SiteContent)
var mu sync.Mutex
var wg sync.WaitGroup
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
if ctx.Err() != nil { return }
select {
case <-ctx.Done(): return
case url, ok := <-urls:
if !ok { return }
if ctx.Err() != nil { return }
content := DownloadSiteContent(ctx, url)
mu.Lock()
result[url] = content
mu.Unlock()
}
}
}()
}
wg.Wait()
return result
}
Функция ждёт текущие загрузки и возвращает частичные результаты при отмене. Для быстрой остановки DownloadSiteContent должна сама использовать отменяемое ожидание/HTTP-контекст. Производитель urls тоже обязан реагировать на отмену. Повторные URL сохраняют последний завершившийся результат; API без error не сообщает причину неполноты.