我们都知道通道(channel)分两种:
- 缓冲通道
- 非缓冲通道
// 缓冲通道
ch1 := make(chan int, 10)
ch2 := make(chan bool, 2)
// 非缓冲通道
ch3 := make(chan int)
ch4 := make(chan bool, 0)
发送通道数据
// 创建一个空接口通道,注意定义的通道类型有 |
接收通道数据
阻塞接收数据
data := <-ch
执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。
非阻塞接收数据
// data:表示接收到的数据。未接收到数据时,data 为通道类型的零值
// ok:表示是否接收到数据。
data, ok := <-ch非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要实现接收超时检测,可以配合 select 和计时器 channel 进行,可以参见后面的内容。
接收任意数据,忽略接收的数据
<-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)
if data == 0 {
break
}
}
}
关闭通道
ch := make(chan string) |
通道特性:
- 同一个通道,发送操作之间是互斥的,接收操作之间也是互斥的(并发安全)
- 发送操作和接收操作中对元素值的处理都是不可分割的。
- 发送操作在完全完成之前会被阻塞,接收操作也是一样。
- 对于缓冲通道:如果通道已满,那么对它的所有发送操作都会被阻塞,直到通道中有元素值被接收走;如果通道已空,那么对它的所有接收操作都会被阻塞,直到通道中有新的元素值出现。
对于非缓冲通道:无论是发送操作还是接收操作,一开始执行就会被阻塞,直到配对的操作也开始执行,才会继续传递。
注意点:
关闭通道要在发送方关闭,关闭后如果channel内还有元素,并不会对接下来的接收产生影响
单向通道最主要的用途就是约束其他代码的行为
通过函数的参数类型或者返回值类型来限制(Go的语法糖)。
func(ch chan<- int) //传入双向通道,在函数里面调用ch只能发送 |
那么非缓冲通道是不是就是缓冲size为1的缓冲通道呢?我们来测试下:
func TestChan(t *testing.T) { |
go test 运行一下,会发现TestChan阻塞住了,ch <- 1 不能写在go func 前面,那么非缓冲通道显然不是就是缓冲size为1的缓冲通道。非缓冲通道像是以前的快递员,必须有收货人当面接收成功,否则他会等着你送货,有缓冲通道更像是现在的快递员,直接放在驿站或者快递柜,除非驿站快递柜满了,否则他就胡塞,不管你在不在家,有没有接收成功。