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  }