目录
1.简介
2.channel类型
无缓冲区的channel
无缓冲区channel的创建
带缓冲区的channel
带缓冲区channel的创建
3.channel使用代码演示
4.获取channel中的值
编辑
5.单向channel
单向发送data,发送到channel中
单向接收,channel接收数据
6.channel使用中的死锁
1.简介
channel通道是一种比较重要的数据结构,用于多个协程之间的通信。
可以把channel想象成一个传送带,将协程想象成传送带周边的人,一个人往传送带上放东西,传送带另一端的人将传送带的东西取走。
也就是左边的goroutine是负责发送数据的,右边的goroutine是负责接收数据的。data会通过channel,从左边的goroutine传送的右边的goroutine。
2.channel类型
channel类型分为两种类型,一种是无缓冲的channel,一种是有缓冲的channel。
无缓冲区的channel
对于无缓冲区的channel来说,channel本身是不存储数据的,整个channel的发送和接收都会被阻塞。
如下图,左边协程发送数据到右边协程,当data还没有被右边的协程接收处理完的时候,左边的协程会被阻塞发送数据,左边的协程发送的data会被阻塞到外面,只有当右边的协程获取数据之后,左边的协程才能继续将数据发送出去。
无缓冲区channel的创建
①先声明再实例化
var c chan interface{}
c = make(chan interface{})
②直接实例化
c := make(chan interface{})
带缓冲区的channel
带缓冲区的channel会有暂存数据的功能,当channel缓冲区满了或者空(空的话可以理解为是不带缓冲区的channel)的时候,channel的发送和接收会被阻塞。
如下图:
channel为空的话,相当于是无缓冲区的channel。
假设定义一个缓冲区大小为3的channel,向里面发送三个data,当达到缓冲区的大小的时候,发送端的协程的数据会被阻塞,直到接收端的协程接收玩一个data数据,发送端的协程才可以发送下一个数据。
带缓冲区channel的创建
与无缓冲区channel不同的是,带缓冲区的channel需要指定缓冲区大小。
①先声明,后初始化,创建一个缓冲区大小为2的channel
var c chan interface{}
c = make(chan interface{},2)
②直接创建
c := make(chan interface{},2)
3.channel使用代码演示
func main() {
// 定义一个无缓冲的channel
ch := make(chan int)
// 开启一个协程,往里面添加1
go func() {
ch <- 1
}()
// 主协程获取channel里面的data
v := <-ch
fmt.Printf("v is %d",v)
}
那么无缓冲的协程阻塞怎么做到的呢?
首先创建一个无缓冲的通道 ,发送端发送一个,接收端接收一个值。
go协程和主协程是并发运行的,使用go关键字开启一个协程是需要时间初始化的,所以先执行的是
func main() {
ch := make(chan string)
s := []string{"A","B","C","D"}
// 协程的创建需要一些时间
go func() {
// 执行玩,关闭chan,要不然主线程会一直从chan中获取空数据,导致panic
defer close(ch)
for _, v := range s {
fmt.Printf("send to chan %v\n",v)
ch <- v
// 每间隔1秒往chan中发送一条数据
time.Sleep(1 * time.Second)
}
}()
fmt.Println("5秒开始")
time.Sleep(5*time.Second)
fmt.Println("5秒结束")
// 获取chan中的数据
for val := range ch {
fmt.Printf("received %v\n",val)
}
}
执行的结果:
首先执行主线程,5s开始,间隔5s,此时go线程初始化好,执行go协程中的内容,发送数据到通道,每间隔1s往chan中发送一条数据。此时主协程接收chan中的数据。
那么有缓冲区的通道呢?
定义一个大小为2的有缓冲区的通道,当无缓冲区的时候,可以传送一个,加上缓冲区的大小
func main() {
ch := make(chan string,2)
s := []string{"A","B","C","D"}
go func() {
defer close(ch)
for _, v := range s {
fmt.Printf("send to chan %v\n",v)
ch <- v
// 每间隔1s发送一个数据
time.Sleep(1*time.Second)
}
}()
fmt.Println("5s开始")
time.Sleep(5*time.Second)
fmt.Println("5s结束")
for c := range ch {
fmt.Printf("received %v\n",c)
}
}
4.获取channel中的值
方式一:
<-ch
方式二:
for range
func main() {
ch := make(chan int)
go func() {
// defer close(ch) // 此时ok是false
ch <-1
}()
// 获取管道中的值,一个是从管道中获取发送方发送的值,另一个是go中默认的一个值
// ok的意思是成功从管道中获取值,ok就是true,否则就为false
v, ok := <-ch
fmt.Printf("%v is %v\n",v,ok)
}
5.单向channel
发送还是接收,具体看通道变量c 后面的变量
单向发送data,发送到channel中
var c chan<- interface{}
c = make(chan<- interface{})
c := make(chan<- interface{})
单向接收,channel接收数据
var c <-chan interface{}
c = make(<- chan interface{})
c := make(<- chan interface{})
6.channel使用中的死锁
对于一个已经阻塞的channel来说,如果继续发送或者接收,会出现死锁运行中的错误。
非缓冲区的只有发送或者接收,会出现阻塞,产生死锁