github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/prepare/18channeluse/fanin/main.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "reflect" 6 "sync" 7 ) 8 9 /* 10 扇入模式(FanIn)是将多个同样类型的输入channel合并成一个同样类型的输出channel,也就是channel的合并。 11 12 */ 13 14 // https://github.com/campoy/justforfunc/blob/master/27-merging-chans/main.go 15 //1.Goroutine方式 每个channel起一个goroutine。 16 func fanIn(chans ...<-chan interface{}) <-chan interface{} { 17 out := make(chan interface{}) 18 go func() { 19 var wg sync.WaitGroup 20 wg.Add(len(chans)) 21 22 for _, c := range chans { 23 go func(c <-chan interface{}) { 24 for v := range c { 25 out <- v 26 } 27 wg.Done() 28 }(c) 29 } 30 31 wg.Wait() 32 close(out) 33 }() 34 return out 35 } 36 37 //2. Reflect 方式 38 // 利用反射库针对select语句的处理合并输入channel。在输入channel读取比较均匀的时候比较有效,否则性能比较低下 39 func fanInReflect(chans ...<-chan interface{}) <-chan interface{} { 40 out := make(chan interface{}) 41 go func() { 42 defer close(out) 43 var cases []reflect.SelectCase 44 for _, c := range chans { 45 cases = append(cases, reflect.SelectCase{ 46 Dir: reflect.SelectRecv, 47 Chan: reflect.ValueOf(c), 48 }) 49 for len(cases) > 0 { 50 i, v, ok := reflect.Select(cases) 51 if !ok { //remove this case 52 cases = append(cases[:i], cases[i+1:]...) 53 continue 54 } 55 out <- v.Interface() 56 } 57 } 58 }() 59 return out 60 } 61 62 //3.递归方式 63 //这种方式虽然理解起来不直观,但是性能还是不错的(输入channel不是很多的情况下递归层级不会很高,不会成为瓶颈) 64 func fanInRec(chans ...<-chan interface{}) <-chan interface{} { 65 switch len(chans) { 66 case 0: 67 return nil 68 case 1: 69 return chans[0] 70 case 2: 71 return mergeTwo(chans[0], chans[1]) 72 default: 73 m := len(chans) / 2 74 return mergeTwo( 75 fanInRec(chans[:m]...), // 左边 76 fanInRec(chans[m:]...)) // 右边 77 } 78 } 79 func mergeTwo(a, b <-chan interface{}) <-chan interface{} { 80 c := make(chan interface{}) 81 go func() { 82 defer close(c) 83 for a != nil || b != nil { 84 select { 85 case v, ok := <-a: 86 if !ok { 87 a = nil 88 continue 89 } 90 c <- v 91 case v, ok := <-b: 92 if !ok { 93 b = nil 94 continue 95 } 96 c <- v 97 } 98 } 99 }() 100 return c 101 } 102 103 func asStream(done <-chan struct{}) <-chan interface{} { 104 s := make(chan interface{}) 105 values := []int{1, 2, 3, 4, 5} 106 go func() { 107 defer close(s) 108 109 for _, v := range values { 110 select { 111 case <-done: 112 return 113 case s <- v: 114 } 115 } 116 117 }() 118 return s 119 } 120 121 func main() { 122 fmt.Println("fanIn by goroutine:") 123 done := make(chan struct{}) 124 ch := fanIn(asStream(done), asStream(done), asStream(done)) 125 for v := range ch { 126 fmt.Println(v) 127 } 128 129 fmt.Println("fanIn by reflect:") 130 ch = fanInReflect(asStream(done), asStream(done), asStream(done)) 131 for v := range ch { 132 fmt.Println(v) 133 } 134 135 fmt.Println("fanIn by recursion:") 136 ch = fanInRec(asStream(done), asStream(done), asStream(done)) 137 for v := range ch { 138 fmt.Println(v) 139 } 140 141 }