channel基础
channel是一种go协程用以接收或发送消息的安全的消息队列,channel就像两个go协程之间的先进先出的导管,来实现各种资源的同步.channel是用来在不同的 goroutine 中交换数据的,千万不要把 Channel 拿来在同一个 goroutine 中的不同函数之间间交换数据.
Channel 操作符<- 和操作方式
通信操作符 <- 的箭头指示数据流向,箭头指向哪里,数据就流向哪里,它是一个二元操作符,可以支持任意类型,对于 channel 的操作只有4种方式:
-
创建 channel (通过make()函数实现,包括无缓存 channel 和有缓存 channel);
-
向 channel 中添加数据(channel<-data);
-
从 channel 中读取数据(data<-channel);
- data<-channel, 从 channel 中接收数据并赋值给 data
- <-channel,从 channel 中接收数据并丢弃
-
关闭 channel(通过 close()函数实现)
- 读取关闭后的无缓存通道,不管通道中是否有数据,返回值都为 0 和 false。
- 读取关闭后的有缓存通道,将缓存数据读取完后,再读取返回值为 0 和 false。
- 对于一个关闭的 channel,如果继续向 channel 发送数据,会引起 panic
- channel 不能 close 两次,多次 close 会 panic
channel类型
无缓冲channel
无缓冲的 Channel 是最常见的形式,当你发送数据到 Channel 时,发送操作会阻塞,直到另一个 goroutine 从该 Channel 接收数据。
ch := make(chan int)
无缓冲的 Channel 会在发送和接收端都准备好之前阻塞
有缓冲channel
带缓冲的 Channel 实际上是一个阻塞队列。队列满时写协程会阻塞,队列空时读协程阻塞.
ch := make(chan int, 5)
-
make(chan int, 5)创建了一个缓冲区大小为5的 Channel。 -
可以向这个 Channel 发送最多
5个数据而不会阻塞。如果缓冲区已满,发送操作会阻塞,直到有数据被接收。
只发送或只接收的 Channel
-
chan<- int表示该 Channel 只能发送数据。 -
<-chan int表示该 Channel 只能接收数据。
有时你可能需要限制 Channel 的方向性,使得它只能发送或接收数据。这可以通过在函数参数中指定 Channel 的方向来实现.方向性 Channel 在 Go 语言中主要用于提高代码的安全性和可读性,通过限制 Channel 的数据流向来防止误用。
// 生产者函数,只发送数据
func producer(ch chan<- int) {
for i := 0; i < 5; i++ {
fmt.Printf("Producing %d\n", i)
ch <- i
time.Sleep(time.Second)
}
close(ch) // 关闭 Channel,表示不再发送数据
}
// 消费者函数,只接收数据
func consumer(ch <-chan int) {
for value := range ch {
fmt.Printf("Consuming %d\n", value)
}
}
func main() {
ch := make(chan int)
go producer(ch) // 启动生产者 goroutine
consumer(ch) // 启动消费者函数(可以在同一个 goroutine 中执行)
}
channel的遍历
for range
channel支持 for range 的方式进行遍历:
func main() {
ci := make(chan int, 5)
for i := 1; i <= 5; i++ {
ci <- i
}
close(ci)
for i := range ci {
fmt.Println(i)
}
}
值得注意的是,在遍历时,如果channel 没有关闭,那么会一直等待下去;如果在遍历时channel已经关闭,那么在遍历完数据后自动退出遍历。也就是说,for range 的遍历方式时阻塞型的遍历方式。
for select
select可以处理非阻塞式消息发送、接收及多路选择。
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
c1 <- "one"
}()
go func() {
time.Sleep(2 * time.Second)
c2 <- "two"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}
}
//received one
//received two
select中有case代码块,用于channel发送或接收消息,任意一个case代码块准备好时,执行其对应内容;多个case代码块准备好时,随机选择一个case代码块并执行;所有case代码块都没有准备好,则等待;还可以有一个default代码块,所有case代码块都没有准备好时执行default代码块。
引用文章