这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
channel
介绍
Channel是 Golang 在语言级别上提供的 goroutine 间的通讯方式,可以使用channel在多个 goroutine 之间传递消息。如果说 goroutine 是 Go 程序并发的执行体,channel 就是它们之间的连接。channel 是可以让一个 goroutine 发送特定值到另一个 goroutine 的通信机制。 Golang 的并发模型是 CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。 Go 语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明 channel 的时候需要为其指定元素类型。
声明
声明的方法如下
var 变量名称 chan 元素类型
声明后的通道不能直接使用,我们需要make来分配内存
格式为:通道实例 := make(chan 元素类型, [缓冲大小])
如果不设置缓冲大小,那么将声明一个无缓冲区通道,该通道只有sender和receiver都准备好时,才能进行通信。
channel操作
发送(send)
通过 <- 操作符来向通道内发送数据 例如:
func main() {
//创建通道ch
ch := make(chan int,2)
//从通道里面发送数据
ch <- 10 //将10发送到通道ch中
}
接收(receive)
通过 <- 操作符从通道中接收数据
例如:x := <- ch 代表用x变量接受了通道ch内的一个数据
关闭(close)
格式为:close(ch)
读取通道内的数据
通过for range 来读取通道内所有的数据(当通道还未关闭时)
单向channel
只读通道(只能从通道中读取)
声明格式为:var ch <- chan int
只写通道(只能从通道中读取)
声明格式为:var ch chan <- int
select关键字
select只能用于io操作,一般配合channel使用。 例子:
select {
case msg1 := <-c1:
fmt.Println("c1 received:",msg1)
case msg2 := <-c2:
fmt.Println("c2 received",msg2)
default:
fmt.Println("No data received")
}
意思为哪个管道先有数据就先执行哪个分支,select不保证顺序执行,是随机执行的
通过channel实现广播
可利用已关闭的channel读取数据时总是非阻塞的特性,可以实现在一个协程中向其他多个协程广播某个事件发生的通知,例如定义一个exitp chan any来控制所有go程退出
package main
import (
"fmt"
"time"
)
type aa struct {
message chan any
name string
}
func (p *aa) Run() {
defer close(p.message)
for {
select {
case ss := <-p.message:
fmt.Println(p.name, ss)
case <-Exitp:
fmt.Println("exit :", p.name)
return
}
}
}
var Eventbus chan any
var Exitp chan any
var Reicevermap map[string]*aa
func main() {
Exitp = make(chan any) //控制go程退出
Eventbus = make(chan any, 1)
Reicevermap = make(map[string]*aa)
a := &aa{make(chan any, 3), "aa"} //aa接收者
go a.Run() //aa接收广播线程
Reicevermap[a.name] = a
b := &aa{make(chan any, 3), "bb"} //bb接收者
go b.Run() //bb接收广播线程
Reicevermap[b.name] = b
fmt.Println("111")
go broadcast()
time.Sleep(time.Duration(1) * time.Second)
Eventbus <- "第一个广播"
time.Sleep(time.Duration(3) * time.Second)
close(Exitp)
time.Sleep(time.Duration(3) * time.Second)
}
func broadcast() {
for event := range Eventbus {//不建议此种方式接收chan数据,建议用select
for _, v := range Reicevermap {
v.message <- event
}
}
}