一.基础内容
1.声明通道的两种方式
1.变量方式
var ch chan int //声明一个存放int类型数据的通道
通过这种方式声明的通道,值为nil.
2.使用内置函数make
ch1 := make(chan int) //声明一个无缓冲的通道
ch2 := make(chan int , 5) //声明一个带有缓冲的通道
2.管道的数据读写
ch := make(chan int , 5)
//向通道内写数据
ch <- 10
//从通道中读数据
a := <- ch
管道默认是双向读写的,但是可以声明单向通道,设置为只读或者只写
func main() {
ch := make(chan int, 5)
//写入数据
onlyWrite(ch)
//读数据
onlyRead(ch)
}
// 只能写数据的通道
func onlyWrite(write chan<- int) {
write <- 1
write <- 2
}
// 只能读数据的通道
func onlyRead(read <-chan int) {
a := <-read
b := <-read
fmt.Println(a, b) //1 2
}
注意事项:
1.管道如果没有缓冲区,向管道写入数据会造成阻塞,直到有协程从管道内读数据.同理,从没有缓冲区的管道读数据也会造成阻塞,直到有协程向管道内写数据.从这个特性来看无缓冲通道,也可以被称为同步通道.
func main() {
ch := make(chan int)
//开启协程 , 从管道内读数据
go read(ch) //协程会被阻塞,直到向管道内写数据
//向管道写数据
ch <- 1
}
func read(ch chan int) {
fmt.Println("开始读取数据")
a := <-ch
fmt.Println(a)
}
2.管道如果有缓冲区,但是没有数据,从管道读数据也会造成阻塞.如果缓冲区已满,向管道写数据同样会造成阻塞.
3.对于值为nil的通道,无论是读写都会造成阻塞,而且是永久阻塞
//两个协程无法结束,永久阻塞
var sg sync.WaitGroup
func main() {
var ch chan int
sg.Add(2)
go write(ch)
go read(ch)
sg.Wait()
fmt.Println("结束")
}
func write(ch chan int) {
fmt.Println("写数据")
ch <- 1
fmt.Println("写数据结束")
}
func read(ch chan int) {
fmt.Println("开始读取数据")
a := <-ch
fmt.Println(a)
}
4.对于一个已经被关闭,但是还有数据的通道,是可以读取数据的.但是不能向被关闭的通道写数据.
二. 原理
1.环形队列
2.等待队列
2.2.1 等待读队列(recvq).
从管道内读取数据时,如果管道没有缓冲区,或者缓冲区为空,当前协程会被加入到等待读队列.
2.2.2 等待写队列(sendq)
向管道内写数据时,如果管道缓冲区已满,则将当前协程加入到等待写队列.进入睡眠并等待被读协程唤醒.