Дан код, main трогать нельзя. Нужно реализовать только ExecutePipeline: она
получает стадии конвейера и должна связать их каналами так, чтобы выход каждой
стадии стал входом следующей.
type stageFn func(in chan int, out chan int)
func ExecutePipeline(stages ...stageFn)
func main() {
ExecutePipeline(
func(in, out chan int) {
for _, v := range []int{1, 2, 3, 4, 5} {
out <- v
}
},
func(in, out chan int) {
for v := range in {
out <- v * 2
time.Sleep(100 * time.Millisecond)
}
},
func(in, out chan int) {
for v := range in {
fmt.Println(v)
}
},
)
}
Требования
- Все стадии работают одновременно: стадия
i+1 получает значение сразу,
как только стадия i его отправила, а не после её завершения.
out стадии i — это in стадии i+1. Каналы небуферизованные.
- Стадия сама свои каналы не закрывает. Когда функция стадии вернулась,
ExecutePipeline закрывает её out, чтобы range in у следующей
стадии закончился.
- Первая стадия получает в
in уже закрытый пустой канал.
- Последняя стадия тоже может писать в свой
out. Эти значения никому не
нужны: ExecutePipeline вычитывает и отбрасывает их, чтобы стадия не
зависла на отправке.
ExecutePipeline возвращается только после того, как вернулись все
стадии. Без стадий возвращается сразу.
- После возврата не остаётся горутин. Код проходит
go test -race.
Пример
Для main выше программа печатает 2 4 6 8 10 (каждое число на своей строке)
и завершается примерно за 500 мс.
Устно: что будет, если стадия перестала читать in, не дочитав его до
конца? Как добавить отмену через context, если сигнатуру stageFn менять можно?
Разбор и эталонное решение — после своей попытки: на собеседовании подсказок не будет.
Сначала попробуй сам — подсказки не дают готовый ответ.