先入先出
如果说goroutine是Go程序并发的执行体,channel就是它们之间的连接。channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制。
二、通道产生的原因竞态
通信共享内存
三、声明channel
语法:
var 变量 chan 元素类型
例:
var ch1 chan int // 声明一个传递整型的通道
var ch2 chan bool // 声明一个传递布尔型的通道
var ch3 chan []int // 声明一个传递int切片的通道
四、创建channel
语法:
make(chan 元素类型, [缓冲大小])
注意: 声明的通道后需要使用make函数初始化之后才能使用。channel的缓冲大小是可选的。
例:
ch4 := make(chan int, 3)
ch5 := make(chan bool)
ch6 := make(chan []int)
五、channel相关操作
1、发送值
将一个值发送到通道中。
例:
ch <- 10 // 把10发送到ch中
2、接收值
从一个通道中接收值。
例:
x := <- ch // 从ch中接收值并赋值给变量x
<-ch // 从ch中接收值,忽略结果
3、关闭通道
例:
close(ch)
3.1 注意
发送完毕垃圾回收机制不是必须
3.2 特点
- 对一个关闭的通道再发送值就会导致panic。
- 对一个关闭的通道进行接收会一直获取值直到通道为空。
- 对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
- 关闭一个已经关闭的通道会导致panic。
1、无缓冲通道
必须有接收才能发送
语法:
ch := make(chan type)
例:
func recv(c chan int) {ret := <-cfmt.Println("接收成功", ret)
}
func main() {ch := make(chan int)go recv(ch) // 启用goroutine从通道接收值ch <- 10fmt.Println("发送成功")
}
分析: 无缓冲通道上的发送操作会阻塞,直到另一个goroutine在该通道上执行接收操作,这时值才能发送成功,两个goroutine将继续执行。相反,如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值。
2、有缓冲通道
只要通道的容量大于零,那么该通道就是有缓冲的通道,通道的容量表示通道中能存放元素的数量。就像你小区的快递柜只有那么个多格子,格子满了就装不下了,就阻塞了,等到别人取走一个快递员就能往里面放一个。
语法:
ch := make(chan type, [cap])
例:
func main() {ch := make(chan int, 1) // 创建一个容量为1的有缓冲区通道ch <- 10fmt.Println("发送成功")
}
五、单向通道
有的时候我们会将通道作为参数在多个任务函数间传递,很多时候我们在不同的任务函数中使用通道都会对其进行限制,比如限制通道在函数中只能发送或只能接收。
语法:
chan<- int 是一个只能发送的通道,可以发送但是不能接收;
<-chan int 是一个只能接收的通道,可以接收但是不能发送。
例:
func counter(out chan<- int) {for i := 0; i < 100; i++ {out <- i}close(out)
}func squarer(out chan<- int, in <-chan int) {for i := range in {out <- i * i}close(out)
}
func printer(in <-chan int) {for i := range in {fmt.Println(i)}
}func main() {ch1 := make(chan int)ch2 := make(chan int)go counter(ch1)go squarer(ch2, ch1)printer(ch2)
}
注意: 在函数传参及任何赋值操作中将双向通道转换为单向通道是可以的,但反过来是不可以的。