前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go 开发常用操作技巧--channel

Go 开发常用操作技巧--channel

作者头像
微客鸟窝
发布2023-01-09 19:21:03
4480
发布2023-01-09 19:21:03
举报
文章被收录于专栏:Go语言指北Go语言指北

在 Go 语言中,提倡使用通信来共享内存,而不是通过共享内存来通信,这里的通信就是通过 channel 发送接收消息的方式进行数据传递,而不是通过修改同一个变量。所以在数据流动、传递的场景中要考虑优先使用 channel,它是并发安全的,性能也不错。

channel 声明

代码语言:javascript
复制
ch := make(chan string)
  • 使用 make 函数
  • chan 是关键字,表示 channel 类型,chan 是一个集合类型
  • string 表示 channel 里存放数据的类型

在声明 channel 时,需要指定被共享的数据类型,其中可以通过 channel 共享的类型有 内置类型、命名类型、结构类型和引用类型的值或指针。

channel 使用

chan 只有发送和接收两种操作:

发送:<-chan //向chan内发送数据 接收:chan-> //从chan中获取数据

channel 分为有缓冲和无缓冲两种,下面来分别介绍下。

无缓冲 channel

无缓冲 channel,通道的容量是0,它不能存储数据,只是起到了传输的作用,所以无缓冲 channel 的发送和接收操作是同时进行的。

示例:

代码语言:javascript
复制
package main

import (
 "fmt"
)

func main() {
 ch := make(chan string)
 go func(){
  fmt.Println("微客鸟窝")
  ch <- "执行完毕"
 }()

 fmt.Println("我是无尘啊")
 value := <-ch
 fmt.Println("获取的chan的值为:",value)
}

运行结果:

代码语言:javascript
复制
我是无尘啊
微客鸟窝
获取的chan的值为: 执行完毕

有缓冲 channel

在声明的时候,我们可以传入第二个参数,即「channel容量大小」,这样就是创建了一个有缓冲 channel。

  • 有缓冲 channel 内部有一个队列
  • 发送操作是向队列尾部追加元素,如果队列满了,则阻塞等待,直到接收操作从队列中取走元素。
  • 接收操作是从队列头部取走元素,如果队列为空,则阻塞等待,直到发送操作向队列追加了元素。
  • 可以通过内置函数 cap() 来获取 channel 的容量,通过内置函数 len() 获取 channel 中元素个数。
代码语言:javascript
复制
ch := make(chan int,3)
ch <- 1
ch <- 2
fmt.Println("容量为",cap(ch),"元素个数为:",len(ch))
//打印结果:容量为 3 元素个数为:2

关闭 channel

使用内置函数 close(ch)

对端可以判断channel是否关闭

代码语言:javascript
复制
if num,ok := <- ch;ok == true{
 //如果对端已经关闭,ok-->false
 //如果对端没有关闭,ok-->true
}

也可以使用range 替代ok

代码语言:javascript
复制
//当channel关闭时,range方式会将里面剩余的数据全部读取完成,再退出
//ch不能替换为<-ch ; 
for num := range ch{
  fmt.Println("读到数据:",num)
}

channel 关闭了就不能再向其发送数据了,否则会引起 panic 异常。 可以从关闭了的 channel 中接收数据,如果没数据,则接收到的是元素类型的零值。

单向 channel

只能发送或者只能接收的 channel 为单向 channel。

代码语言:javascript
复制
send := make(ch<- int) //只能发送数据给channel
receive := make(<-ch int) //只能从channel中接收数据

示例:

代码语言:javascript
复制
package main

import (
 "fmt"
)
//只能发送通道
func send(s chan<- string){
 s <- "微客鸟窝"
}
//只能接收通道
func receive(r <-chan string){
 str := <-r
 fmt.Println("str:",str)
}
func main() {
 //创建一个双向通道
 ch := make(chan string)
 go send(ch)
 receive(ch)
}

//运行结果: str: 微客鸟窝

实战技巧

控制 goroutine 并发的数量

代码语言:javascript
复制
func main(){
 m1 := make(chan bool)
 go func() {
   for{
   select {
   case <-m1:
    fmt.Println("close")
    return
   case <-time.After(time.Millisecond*10000):
    fmt.Println("-----")
   }
  }
 }()

 // 控制 goroutine 并发的数量
 limit := make(chan bool,3)
 wg := sync.WaitGroup{}
 for i :=0;i<10;i++{
  limit <- true
  wg.Add(1)
  go func(i int) {
   defer func() {
    <- limit
    wg.Done()
   }()
   //HandleLogic()
   fmt.Println(i)
  }(i)
 }
 wg.Wait()

 m1 <- true
 close(m1)
}

for select 无限循环模式

一般是和 channel 组合完成任务,格式为:

代码语言:javascript
复制
for { //for 无限循环,或者使用 for range 循环
  select {
    //通过 channel 控制
    case <-done:
      return
    default:
      //执行具体的任务
  }
}

这种是 for + select 多路复用的并发模式,哪个 case 满足条件就执行对应的分支,直到有满足退出的条件,才会退出循环。没有退出条件满足时,则会一直执行 default 分支。

select timeout 模式

假如一个请求需要访问服务器获取数据,但是可能因为网络问题而迟迟获取不到响应,这时候就需要设置一个超时时间:

代码语言:javascript
复制
package main

import (
 "fmt"
 "time"
)

func main() {
 result := make(chan string)
 timeout := time.After(3 * time.Second) //3秒后超时
 go func() {
  //模拟网络访问
  time.Sleep(5 * time.Second)
  result <- "服务端结果"
 }()
 for {
  select {
  case v := <-result:
   fmt.Println(v)
  case <-timeout:
   fmt.Println("网络访问超时了")
   return
  default:
   fmt.Println("等待...")
   time.Sleep(1 * time.Second)
  }
 }
}

运行结果:

代码语言:javascript
复制
等待...
等待...
等待...
网络访问超时了

select timeout 模式核心是通过 time.After 函数设置的超时时间,防止因为异常造成 select 语句无限等待。 注意:不要写成这样 case <- time.After(time.Second), 这样是本次监听动作的超时时间,意思就说,只有在本次 select 操作中会有效,再次 select 又会重新开始计时,但是有default ,每次都会走到 default,那case 超时操作,肯定执行不到了。


图片及部分相关技术知识点来源于网络搜索,侵权删!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-12-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微客鸟窝 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • channel 声明
  • channel 使用
    • 无缓冲 channel
      • 有缓冲 channel
      • 关闭 channel
      • 单向 channel
      • 实战技巧
        • 控制 goroutine 并发的数量
          • for select 无限循环模式
            • select timeout 模式
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档