Sobes.tech
Senior

Αποστολή Merge channel Υλοποιήστε τη λειτουργία merge, η οποία δέχεται έναν αυθαίρετο αριθμό καναλιών chan int και επιστρέφει ένα έξοδο κανάλι, από το οποίο μπορείτε να διαβάσετε όλες τις τιμές από τα δοθέντα κανάλια. Τα δοθέντα κανάλια εκτός της λειτουργίας merge κλείνουν μετά την εγγραφή. Μετά την ολοκλήρωση της ανάγνωσης από το συγχωνευμένο κανάλι, το πρόγραμμα πρέπει να εμφανίζει "ok". Η λειτουργία merge δεν πρέπει να διαρκεί περισσότερο από 1 δευτερόλεπτο, τα πηγαια κανάλια μπορεί να μην κλείσουν.

sobes.tech AI

Απάντηση από 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, как указано в условии.