github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/prepare/18channeluse/or_channel_go/main.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  )
     8  
     9  /*
    10  提供相同服务的n个节点发送请求,只要任意一个服务节点返回结果,
    11  我们就可以执行下面的业务逻辑,其它n-1的节点的请求可以被取消或者忽略
    12  */
    13  
    14  //or函数可以处理n个channel,它为每个channel启动一个goroutine,
    15  // 只要任意一个goroutine从channel读取到数据,输出的channel就被关闭掉了。
    16  func or(chans ...<-chan interface{}) <-chan interface{} {
    17  	out := make(chan interface{})
    18  	go func() {
    19  		var once sync.Once
    20  		for _, c := range chans {
    21  			go func(ch <-chan interface{}) {
    22  				select {
    23  				case <-ch:
    24  					once.Do(func() { // 为了避免并发关闭输出channel的问题,关闭操作只执行一次
    25  						close(out)
    26  					})
    27  				case <-out:
    28  				}
    29  			}(c)
    30  		}
    31  	}()
    32  	return out
    33  }
    34  
    35  func main() {
    36  	sig := func(after time.Duration) <-chan interface{} {
    37  		c := make(chan interface{})
    38  		go func() {
    39  			defer close(c)
    40  			time.Sleep(after)
    41  		}()
    42  		return c
    43  	}
    44  
    45  	start := time.Now()
    46  	<-or(
    47  		sig(10*time.Second),
    48  		sig(5*time.Second),
    49  		sig(10*time.Second),
    50  		sig(10*time.Second),
    51  		sig(10*time.Second),
    52  		sig(01*time.Minute),
    53  	)
    54  	fmt.Printf("done after %v", time.Since(start))
    55  }