扇入扇出
扇入扇出和pipeline的最大区在于,管线是串行的,但是扇入扇出是并行的。并行是指,一个管线可以接收其它多个数据源的输入,前提是管线对于多个数据源的输入顺序是不敏感的。
一个扇入扇出模块示例图如下:
给出代码示例:
package main
import (
"fmt"
"math/rand"
"runtime"
"sync"
"time"
)
func main() {
fanIn := func(done <-chan interface{}, channels ...<-chan interface{}) <-chan interface{} {
multiplexedStream := make(chan interface{})
var wg sync.WaitGroup
multiplex := func(ch <-chan interface{}) { // 并发输入模块
defer wg.Done()
for i := range ch {
select {
case <-done:
return
case multiplexedStream <- i:
}
}
}
wg.Add(len(channels)) // 启动扇入
for _, c := range channels {
go multiplex(c)
}
go func() { // 所有输入都结束时,扇出管线
wg.Wait()
close(multiplexedStream)
}()
return multiplexedStream
}
generator := func(done <-chan interface{}) <-chan interface{} {
genCh := make(chan interface{})
go func() {
defer close(genCh)
for {
select {
case <-done:
return
case genCh <- rand.Intn(100000):
t := rand.Intn(5) + 1 // 模拟工作过程
time.Sleep(time.Second * time.Duration(t))
}
}
}()
return genCh
}
take := func(done <-chan interface{}, valueStream <-chan interface{}, n int) <-chan interface{} {
takeStream := make(chan interface{})
go func() {
defer close(takeStream)
for i := 0; i < n; i++ {
select {
case <-done:
return
case n := <-valueStream:
takeStream <- n
}
}
}()
return takeStream
}
done := make(chan interface{})
defer close(done)
num := runtime.NumCPU()
chs := make([]<-chan interface{}, num)
for i := 0; i < num; i++ {
chs[i] = generator(done)
}
for n := range take(done, fanIn(done, chs...), 20) {
fmt.Printf("%v ", n)
}
fmt.Println("")
}
管线的核心思想在于,每个单独的输入都有一个单独goroutine处理,并写入同一个数据源。