通道 channel
go语言使用channel进行goroutine之间的数据传输,channel在go语言中是一种特殊的数据类型,在任何时候同时只能有一个goroutine访问通道进行发送和获取数据。
声明通道的类型
var ch chan int
ch表示通道变量:通道内的数据类型
int表示通道变量:保存通道的变量
chan的空值是nil,声明后需要配合make后才能使用
创建通道
ch1 := make(chan int) //创建一个整型类型的通道
ch2 := make(chan interface{}) //创建一个空接口类型的通道,可以存放任意格式
type Equip struct{}
ch3 := make(chan *Equip) //创建Equip指针类型的通道,存放Equip类型数据
向通道中发送数据的例子
ch := make(chan interface{})
//将0放入通道中
ch <- 0
//将字符串放入通道中
ch <- "hello world"
使用通道接受数据
通道接收数据同样使用<-符号,通道收发数据在不同的两个goroutine之间进行。
阻塞式接受数据
data := <-ch 该语句在执行时会发生阻塞,直到接收到数据并赋值给data变量。
非阻塞式接收数据
data, ok := <-ch (data表示接收到的数据,未接收到数据时,data表示通道类型的零值)
ok 表示是否接收到数据
接收任意数据,忽略接收的数据
阻塞接收数据后,忽略从通道返回的数据,格式为 <-ch
代码示例如下
package main
import "fmt"
func main(){
//创建通道
ch := make(chan int)
//开启一个goroutine并发函数
go func(){
fmt.Println("Start goroutine")
ch <- 0
fmt.Println("exit goroutine")
}()
<-ch
fmt.Println("all done")
}
以上代码示例中ch<-0表示接收任意数据目的是通知main的goroutine。<-ch表示忽略任意数据目的是等待匿名goroutine。
通道循环接收数据
通道循环接收代码格式如下
for data := range ch {
}
代码格式如下
package main
import ("fmt" "time")
func main(){
ch := make(chan int)
go func(){
for i := 3; i >=0; i--{
ch <- i
//每次发送完等待一定时间
time.Sleep(time.Second)
}
}()
for data := range ch {
fmt.Println(data)
//接收到0时停止
if data == 0{
break
}
}
}
并发打印数据示例
package main
import ("fmt")
func Printer(c chan int) {
for {
//从通道中获取数据
data := <-c
if data == 0 {
break
}
fmt.Printf("%d ",data)
}
//通知main已经循环结束了
c <- 0
}
func main(){
ch := make(chan int)
//并发执行Printer
go Printer(ch)
//循环发送数据
for i := 1; i <= 10; i++{
ch <- i
}
//通知Printer()结束循环了
ch <- 0
//等待Printer()结束
<- ch
创建单向通道
单向通道的声明格式
var ch1 chan<- int
ch1是通道示例名,chan<- 表示只能发送数据的通道 int 表示通道接受的数据
var ch2 <-chan int 这里的<-chan表示只能收到数据通道
带缓冲的通道
为通道增加一个有限大小存储空间的形成带缓冲通道。带缓冲通道在发送时无需等待接收方接收即可完成发送过程,并且不会发生阻塞,只有当存储满的时候才会发生阻塞。
带缓冲的通道创建格式
var ch := make(chan int 3)
这里的ch表示通道的实例, chan 创建通道的关键字, int表示通道数据类型 3表示缓冲容量大小。
带缓冲通道代码示例
package main
import "fmt"
func main(){
ch := make(chan int 3)
fmt.Println(len(ch))
ch <- 1
ch <- 2
ch <-3
fmt.Println(len(ch))
以上代码展示通道ch输入数据前后缓冲的大小。
通道的多路复用–同时处理接收和发送多个通道的数据
多路复用指的是在一个信道上传输多路信号或数据流的过程和技术。
在Go语言中可以使用select关键字,同时响应多个通道的操作。select的每一个case都会对应通道的收发过程。当收发完成时就会触发case中响应的语句。
设置格式如下
select {
case 操作1:
响应语句1
case 操作2:
响应语句2
default:
响应语句3
}
select多路复用中可以接收的样式如下
接收任意数据 case <- ch:
接收变量 case d := <-ch:
发送数据 case ch <-100:
示例:模拟远程调用过程(RPC)
模拟客户端代码
package main
import "time"
//模拟RPC客户端的请求和接收消息封装
func RPCClient(ch chan string, req string) (string, error){
//向服务器发送请求
ch <- req
//等待服务器返回
select{
case ack := <-ch
return ack, nil//返回结果
case <-time.After(time.Second):
return "",errors.New("Time out")//超时
模拟服务器端代码
//模拟RPC服务器端接收客户端请求和回应
func RPCSever(ch chan string){
for {
//接收客户端请求
data := <-ch
//打印接收到的数据
fmt.Println("server received", data)
//向客户端反馈收到
ch <- "roger"
}
}
主程序代码
package main
import "fmt"
func main(){
//创建一个无缓冲字符串通道
ch := make(chan string)
//并发执行服务器逻辑
go RPCServer(ch)
//客户端请求数据和接收数据
recv, err := RPCClient(ch, "hi")
if err != nil {
//发生错误打印
fmt.Println(err)
} else {
//正常接收到数据
fmt.Println("client received", recv)
}
}