Sobes.tech
Senior

Úkol na Merge channel Implementujte funkci merge, která přijímá libovolný počet kanálů chan int a vrací jeden výstupní kanál, ze kterého lze číst všechny hodnoty z předaných kanálů. Předané kanály mimo funkci merge se po zápisu uzavřou. Po dokončení čtení z sloučeného kanálu by program měl vypisovat "ok". Funkce merge by měla trvat nejdéle 1 sekundu, zdrojové kanály se mohou nezavřít.

sobes.tech AI

Odpověď od AI

Для реализации функции merge в Go, которая принимает произвольное количество каналов chan int и возвращает один объединённый канал, из которого можно читать все значения, нужно использовать горутины и select. При этом важно, чтобы функция не зависала, если каналы-источники не закрываются, и чтобы после завершения чтения из объединённого канала программа вывела "ok".

Пример реализации с таймаутом 1 секунда, после которого merge завершится:

package main

import (
    "fmt"
    "time"
)

func merge(channels ...<-chan int) <-chan int {
    out := make(chan int)
    done := make(chan struct{})

    // Запускаем горутину, которая читает из всех каналов
    go func() {
        defer close(out)
        timer := time.NewTimer(time.Second)
        defer timer.Stop()

        for {
            select {
            case <-timer.C:
                // Время вышло — завершаем
                return
            default:
                active := false
                for _, ch := range channels {
                    select {
                    case v, ok := <-ch:
                        if ok {
                            out <- v
                            timer.Reset(time.Second) // сбрасываем таймер при получении данных
                            active = true
                        }
                    default:
                    }
                }
                if !active {
                    // Нет данных во всех каналах, ждем немного
                    time.Sleep(10 * time.Millisecond)
                }
            }
        }
    }()

    return out
}

func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)

    go func() {
        defer close(ch1)
        for i := 0; i < 5; i++ {
            ch1 <- i
            time.Sleep(100 * time.Millisecond)
        }
    }()

    go func() {
        defer close(ch2)
        for i := 100; i < 105; i++ {
            ch2 <- i
            time.Sleep(150 * time.Millisecond)
        }
    }()

    merged := merge(ch1, ch2)
    for v := range merged {
        fmt.Println(v)
    }
    fmt.Println("ok")
}

Важные моменты:

  • Используем таймер 1 секунда, чтобы не зависать, если каналы не закрываются.
  • При получении данных таймер сбрасывается.
  • После окончания чтения из объединённого канала выводим "ok".
  • Каналы-источники закрываются вне функции merge, как указано в условии.