Одновременное скачивание контента с множества веб‑ресурсов

Создайте 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 не сообщает причину неполноты.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее