评论

收藏

[Unix] #yyds干货盘点# Golang的通道复用上手(三)

服务系统 服务系统 发布于:2021-12-26 17:49 | 阅读数:511 | 评论:0

目录



  • 前言
  • 正文
  • 结尾



前言
我们知道 Golang 中的通道都是一端写入,一端读取的。基本上都是一个通道处理自己的逻辑,和别的通道不相关。那么,Golang 中的通道能不能进行统一管理呢?今天我们就来看一看。

正文
既然我们已经打算实现多个通道的统一管理,换句话说就是多路复用,我们需要一个方向。先来看看目前的通道的状态,每个通道都有自己的处理协程。
我们看段代码:
package main
import (
  "fmt"
)
func main() {
  // 定义一个通道ch1
  ch1 := make(chan int)
  // 开启子协程goroutine写入数据
  go func() {
    for i := 0; i < 11; i++ {
      ch1 <- i
      fmt.Println("子协程ch1写入数据:", i)
    }
    close(ch1) //关闭通道
  }()
    
  // 定义一个通道ch2
  ch2 := make(chan int)
  // 开启子协程goroutine写入数据
  go func() {
    for i := 0; i < 11; i++ {
      ch2 <- i
      fmt.Println("子协程ch2写入数据:", i)
    }
    close(ch2) //关闭通道
  }()
  // 主协程读取ch1数据
  for {
    v, ok := <-ch1
    if !ok {
      fmt.Println("读取结束", ok)
      break
    }
    fmt.Println("主协程读取到ch1数据为:", v)
  }
  // 主协程读取ch2数据
  for {
    v, ok := <-ch2
    if !ok {
      fmt.Println("读取结束", ok)
      break
    }
    fmt.Println("主协程读取到ch2数据为:", v)
  }
  fmt.Println("主协程结束")
}
执行结果如下:
子协程ch1写入数据: 0
主协程读取到ch1数据为: 0
主协程读取到ch1数据为: 1
子协程ch1写入数据: 1
子协程ch1写入数据: 2
主协程读取到ch1数据为: 2
主协程读取到ch1数据为: 3
子协程ch1写入数据: 3
子协程ch1写入数据: 4
主协程读取到ch1数据为: 4
主协程读取到ch1数据为: 5
子协程ch1写入数据: 5
子协程ch1写入数据: 6
主协程读取到ch1数据为: 6
主协程读取到ch1数据为: 7
子协程ch1写入数据: 7
子协程ch1写入数据: 8
主协程读取到ch1数据为: 8
主协程读取到ch1数据为: 9
子协程ch1写入数据: 9
子协程ch1写入数据: 10
主协程读取到ch1数据为: 10
读取结束 false
主协程读取到ch2数据为: 0
子协程ch2写入数据: 0
子协程ch2写入数据: 1
主协程读取到ch2数据为: 1
主协程读取到ch2数据为: 2
子协程ch2写入数据: 2
子协程ch2写入数据: 3
主协程读取到ch2数据为: 3
主协程读取到ch2数据为: 4
子协程ch2写入数据: 4
子协程ch2写入数据: 5
主协程读取到ch2数据为: 5
主协程读取到ch2数据为: 6
子协程ch2写入数据: 6
子协程ch2写入数据: 7
主协程读取到ch2数据为: 7
主协程读取到ch2数据为: 8
子协程ch2写入数据: 8
子协程ch2写入数据: 9
主协程读取到ch2数据为: 9
主协程读取到ch2数据为: 10
子协程ch2写入数据: 10
读取结束 false
主协程结束
通过上面的代码,我们也发现每个通道都有自己的协程,然后再和主协程进行数据通讯。这样非常的繁琐,很多代码都是重复的。有没有办法实现通道复用呢?
答案是有的。
我们想要实现的效果是使用一个协程处理所有的通道的通讯,这就需要用到 select 关键字,它都能做些什么呢?
接下来,我们通过一端代码了解一下。
代码实例:
package main
import (
  "fmt"
  "time"
)
func main() {
  ch1 := make(chan int)
  ch2 := make(chan int)
  go func() {
    for {
      select {
      case c1 := <-ch1:
        fmt.Println("成功获取通道ch1的数据:", c1)
      case c2 := <-ch2:
        fmt.Println("成功获取通道ch2的数据:", c2)
      case <-time.After(time.Second * 2):
        //使用time.After 设置超时响应。
        fmt.Println("超时!!")
      }
    }
  }()
  for i := 0; i < 10; i++ {
    ch1 <- i
    fmt.Println("通道ch1写入数据:", i)
  }
  for i := 0; i < 10; i++ {
    ch2 <- i
    fmt.Println("通道ch2写入数据:", i)
  }
  time.Sleep(20) // 等待select所在协程执行结束
}
代码执行结果:
成功获取通道ch1的数据: 0
通道ch1写入数据: 0
通道ch1写入数据: 1
成功获取通道ch1的数据: 1
成功获取通道ch1的数据: 2
通道ch1写入数据: 2
通道ch1写入数据: 3
成功获取通道ch1的数据: 3
成功获取通道ch1的数据: 4
通道ch1写入数据: 4
通道ch1写入数据: 5
成功获取通道ch1的数据: 5
成功获取通道ch1的数据: 6
通道ch1写入数据: 6
通道ch1写入数据: 7
成功获取通道ch1的数据: 7
成功获取通道ch1的数据: 8
通道ch1写入数据: 8
通道ch1写入数据: 9
成功获取通道ch1的数据: 9
成功获取通道ch2的数据: 0
通道ch2写入数据: 0
通道ch2写入数据: 1
成功获取通道ch2的数据: 1
成功获取通道ch2的数据: 2
通道ch2写入数据: 2
通道ch2写入数据: 3
成功获取通道ch2的数据: 3
成功获取通道ch2的数据: 4
通道ch2写入数据: 4
通道ch2写入数据: 5
成功获取通道ch2的数据: 5
成功获取通道ch2的数据: 6
通道ch2写入数据: 6
通道ch2写入数据: 7
成功获取通道ch2的数据: 7
成功获取通道ch2的数据: 8
通道ch2写入数据: 8
通道ch2写入数据: 9
成功获取通道ch2的数据: 9
大家看到,上面的代码,我们只通过一个匿名协程就可以处理所有通道的消息,实现了通道的复用。而且整个过程也非常的明确,管理起来也非常方便。

结尾
其实,Golang的通道的管理是非常复杂的,今后我们在 Golang 的学习过程中,需要重点关注。好了,今天关于通道的介绍就到这里,谢谢大家。


作者简介:
关注下面的标签,发现更多相似文章