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

简介: 在 Go 语言中,提倡使用通信来共享内存,而不是通过共享内存来通信,这里的通信就是通过 channel 发送接收消息的方式进行数据传递,而不是通过修改同一个变量。

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

channel 声明

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

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

channel 使用

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

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

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

无缓冲 channel

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


示例:

package main
import (
 "fmt"
)
func main() {
 ch := make(chan string)
 go func(){
  fmt.Println("微客鸟窝")
  ch <- "执行完毕"
 }()
 fmt.Println("我是无尘啊")
 value := <-ch
 fmt.Println("获取的chan的值为:",value)
}
复制代码

运行结果:

我是无尘啊
微客鸟窝
获取的chan的值为: 执行完毕


有缓冲 channel

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

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

关闭 channel

使用内置函数 close(ch)

对端可以判断channel是否关闭

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

也可以使用range 替代ok

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

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

单向 channel

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

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

示例:

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 并发的数量

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 组合完成任务,格式为:

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

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

select timeout 模式

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

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)
  }
 }
}
复制代码

运行结果:

等待...
等待...
等待...
网络访问超时了
复制代码

select timeout 模式核心是通过 time.After 函数设置的超时时间,防止因为异常造成 select 语句无限等待。

注意:不要写成这样 case <- time.After(time.Second), 这样是本次监听动作的超时时间,意思就说,只有在本次 select 操作中会有效,再次 select 又会重新开始计时,但是有default ,每次都会走到 default,那case 超时操作,肯定执行不到了。

相关文章
|
3月前
|
缓存 弹性计算 API
用 Go 快速开发一个 RESTful API 服务
用 Go 快速开发一个 RESTful API 服务
|
5天前
|
Go 调度 开发者
探索Go语言中的并发模式:goroutine与channel
在本文中,我们将深入探讨Go语言中的核心并发特性——goroutine和channel。不同于传统的并发模型,Go语言的并发机制以其简洁性和高效性著称。本文将通过实际代码示例,展示如何利用goroutine实现轻量级的并发执行,以及如何通过channel安全地在goroutine之间传递数据。摘要部分将概述这些概念,并提示读者本文将提供哪些具体的技术洞见。
|
17天前
|
Go 数据安全/隐私保护 开发者
Go语言开发
【10月更文挑战第26天】Go语言开发
32 3
|
18天前
|
Java 程序员 Go
Go语言的开发
【10月更文挑战第25天】Go语言的开发
26 3
|
28天前
|
安全 Go 调度
探索Go语言的并发之美:goroutine与channel
在这个快节奏的技术时代,Go语言以其简洁的语法和强大的并发能力脱颖而出。本文将带你深入Go语言的并发机制,探索goroutine的轻量级特性和channel的同步通信能力,让你在高并发场景下也能游刃有余。
|
1月前
|
存储 安全 Go
探索Go语言的并发模型:Goroutine与Channel
在Go语言的多核处理器时代,传统并发模型已无法满足高效、低延迟的需求。本文深入探讨Go语言的并发处理机制,包括Goroutine的轻量级线程模型和Channel的通信机制,揭示它们如何共同构建出高效、简洁的并发程序。
|
23天前
|
存储 Go 调度
深入理解Go语言的并发模型:goroutine与channel
在这个快速变化的技术世界中,Go语言以其简洁的并发模型脱颖而出。本文将带你穿越Go语言的并发世界,探索goroutine的轻量级特性和channel的同步机制。摘要部分,我们将用一段对话来揭示Go并发模型的魔力,而不是传统的介绍性文字。
|
29天前
|
安全 Go 调度
探索Go语言的并发模型:Goroutine与Channel的魔力
本文深入探讨了Go语言的并发模型,不仅解释了Goroutine的概念和特性,还详细讲解了Channel的用法和它们在并发编程中的重要性。通过实际代码示例,揭示了Go语言如何通过轻量级线程和通信机制来实现高效的并发处理。
|
1月前
|
安全 Go 调度
探索Go语言的并发之美:goroutine与channel的实践指南
在本文中,我们将深入探讨Go语言的并发机制,特别是goroutine和channel的使用。通过实际的代码示例,我们将展示如何利用这些工具来构建高效、可扩展的并发程序。我们将讨论goroutine的轻量级特性,channel的同步通信能力,以及它们如何共同简化并发编程的复杂性。
|
1月前
|
安全 Go 数据处理
掌握Go语言并发:从goroutine到channel
在Go语言的世界中,goroutine和channel是构建高效并发程序的基石。本文将带你一探Go语言并发机制的奥秘,从基础的goroutine创建到channel的同步通信,让你在并发编程的道路上更进一步。