文章目录
-
- Channel
- 概述
- 定义channel变量
- 无缓冲channel
- 有缓冲channel
- 相关操作
-
- [使用 range 来迭代不断操作channel:](#使用 range 来迭代不断操作channel:)
- 关闭channel
- 单向channel
- 总结
Channel
概述
channel是Go语言中的一个核心类型,可以把它看成管道(pipe)并不完全相同。并发核心单元通过它就可以发送或者接收数据进行通讯
channel是一个数据类型,主要用来解决go程的同步问题以及go程之间数据共享(数据传递)的问题。
goroutine运行在相同的地址空间,因此访问共享内存必须做好同步。goroutine 奉行通过通信来共享内存,而不是共享内存来通信。
引⽤类型 channel可用于多个 goroutine 通讯。其内部实现了同步,确保并发安全。
定义channel变量
和map、切片一样,需要先使用make申请空间
无缓冲channel
- 只有在读端和写端同时工作时,才不会发生阻塞
- 无缓冲取的管道长度是1,读端和写端要同时就绪才能通信
创建格式
go
make(chan Type)
go
//写管道
func WriteChannel(pipe chan int){
i := 1
for {
pipe <- i
i++
}
}
//读管道
func ReadChannel(pipe chan int){
defer fmt.Println("读管道协程结束")
for {
//打印管道中的数据
fmt.Println("管道中读取的数据 ",<-pipe)
time.Sleep(time.Second *1) //休眠1s
}
}
func main(){
//无缓冲管道
c := make(chan int)
go WriteChannel(c)
go ReadChannel(c)
for{
//垃圾回收
runtime.GC()
}
}
有缓冲channel
- 如果管道中的数据被读取完毕后,写端不在写入,读端会阻塞
- 如果管道写满,并且读端关闭,写端就会阻塞
创建格式
go
make(chan Type, capacity)
示例:
go
func WriteChannel_1(pipe chan int){
defer fmt.Println("写管道协程结束")
for i:= 0; i <5; i++{
pipe<-i
}
}
//写管道
func WriteChannel(pipe chan int){
i := 1
for {
pipe <- i
i++
}
}
//读管道
func ReadChannel(pipe chan int){
defer fmt.Println("读管道协程结束")
for {
//打印管道中的数据
fmt.Println("管道中读取的数据 ",<-pipe)
time.Sleep(time.Second *1) //休眠1s
}
}
func main(){
var c = make(chan int, 5)
go WriteChannel(c)
go ReadChannel(c)
for{
//垃圾回收
runtime.GC()
}
}
相关操作
-
获取channel中剩余元素 len(ch)
-
获取缓冲区元素容量大小 len(ch)
go
func main(){
c := make(chan int,5)
// func(){}() 匿名函数
go func(){
i := 0
c<-i
fmt.Printf("len(c)=%d, cap(c)=%d\n", len(c), cap(c))
}()
}
使用 range 来迭代不断操作channel:
go
func main(){
c := make(chan int)
go func(){
for i := 0; i < 5; i++{
f<-i
close(c)
}
}()
for data := range c{
fmt.Println(data)
}
fmt.Println("Finished")
}
关闭channel
close©
go
func IsChannel(pipe chan int){
//使用断言判断,true说明管道没被关闭
if data,ok := <-c;ok{
fmt.Println(data)
}else{
break;
}
}
func WriteChannel(pipe chan int){
i := 0;
for {
pipe<-i
i++
if(i == 5){
close(pipe) //关闭管道 只是当前以及当前下的Goroutine协程不能使用了,不在使用,其他协程还是可以使用的
runtime.GoExot();
}
}
}
func main(){
var c = make(chan int,5)
go WriteChannel(c)
for{}
}
单向channel
单向channel 一般都是在参数中使用的
可以将 channel 隐式转换为单向队列,只收或只发,不能将单向 channel 转换为普通
-
只写cahnnel 格式 chan<-
-
只读channel 格式 <- chan
go
//只写channel
func WriteChannel(pipe chan<- int){
for i := 1; i <5; i++{
pipe<-i;
i++;
}
}
//只读channel
func ReadChannel(pipe <-channel int){
for data := range pipe{
fmt.Println(data)
}
}
func main(){
c := make(chan int,5)
go WriteChannel(c)
go ReadChannel(c)
for{
runtime.GC();
}
}
总结
- channel不像文件一样需要经常去关闭,只有当你确实没有任何发送数据了,或者是想显式的结束range循环之类的,才去关闭channel;
- 关闭channel后,无法向channel 再发送数据(引发 panic 错误后导致接收立即返回零值);
- 关闭channel后,可以继续从channel接收数据;
- 对于nil channel,无论收发都会被阻塞