Sobes.tech
Назад к вопросам
Junior
28

Live coding: задачи на Go — конкурентные запросы, замыкания в горутинах, фильтрация дубликатов, счётчик с потокобезопасностью, буферизированные каналы, таймаут функции

Компании, где спрашивали
Позитив Технолоджис

Ответ от нейросети

sobes.tech AI

В Go часто требуется писать конкурентный код с горутинами и каналами. Рассмотрим ключевые моменты:

  1. Конкурентные запросы — можно запускать несколько горутин, каждая выполняет запрос, результаты собираются через канал.

  2. Замыкания в горутинах — важно правильно захватывать переменные цикла, чтобы избежать ошибок с общим состоянием.

  3. Фильтрация дубликатов — можно использовать map для отслеживания уже встреченных значений.

  4. Счётчик с потокобезопасностью — применяют sync.Mutex или atomic операции.

  5. Буферизированные каналы — позволяют отправлять несколько значений без блокировки отправителя.

  6. Таймаут функции — реализуется через select с time.After.

Пример, который объединяет несколько из этих аспектов:

package main

import (
    "fmt"
    "sync"
    "time"
    "math/rand"
)

func main() {
    // Буферизированный канал для результатов
    results := make(chan int, 5)
    var wg sync.WaitGroup

    // Потокобезопасный счётчик
    var mu sync.Mutex
    counter := 0

    // Множество для фильтрации дубликатов
    seen := make(map[int]bool)

    // Запускаем 5 конкурентных горутин
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()

            // Замыкание с копией id
            time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)
            value := id * 10

            // Отправляем результат в канал с таймаутом
            select {
            case results <- value:
                // Увеличиваем счётчик потокобезопасно
                mu.Lock()
                counter++
                mu.Unlock()
            case <-time.After(300 * time.Millisecond):
                fmt.Printf("Горутина %d: таймаут при отправке\n", id)
            }
        }(i)
    }

    // Закрываем канал после завершения всех горутин
    go func() {
        wg.Wait()
        close(results)
    }()

    // Читаем из канала и фильтруем дубликаты
    for val := range results {
        if !seen[val] {
            fmt.Println("Получено уникальное значение:", val)
            seen[val] = true
        } else {
            fmt.Println("Дубликат пропущен:", val)
        }
    }

    fmt.Println("Всего уникальных значений отправлено:", counter)
}

В этом примере показано:

  • Запуск конкурентных горутин с замыканием (копия id в параметре функции).
  • Использование буферизированного канала для сбора результатов.
  • Таймаут при отправке в канал.
  • Потокобезопасное увеличение счётчика.
  • Фильтрация дубликатов при чтении из канала.

Такой подход часто используется для параллельной обработки и сбора результатов с контролем времени и безопасности.