Golang channel操作详解
序言
对于
channel
的操作始终有着这么一句口诀用于记忆,
空读写阻塞,写关闭异常,读关闭空零
。对于刚开始学习
channel
并不知道具体的解释,所以在查阅了部分资料后,一一做了实验。
1.channel的状态
-
nil
未初始化的状态,只进行了声明,或者手动赋值为nil (
做了var操作但是没有进行make
) -
active/open
正常的channel,可读或者可写 (
make后的有缓冲或者无缓冲channel
) -
closed
已关闭,千万不要误认为关闭channel后,channel的值是nil
2.对
nil
状态和
close
状态的
channel
进行的验证
nil
close
channel
这一部分是对
nil
状态和
close
状态的
channel
进行的验证,结果和口诀一一对应
package main
import (
"fmt"
)
// 空读写阻塞((deadlock!)),写关闭异常,读关闭空零
// nil,未初始化的状态,只进行了声明,或者手动赋值为nil (做了var操作但是没有进行make)
// active(open),正常的channel,可读或者可写 (make后的有缓冲或者无缓冲channel)
// closed,已关闭,千万不要误认为关闭channel后,channel的值是nil
// 空读写阻塞(nil channel)
func nilChanReadWrite(){
var nilChan chan int
// fatal error: all goroutines are asleep - deadlock!
// 编译器提醒 Send may block because of 'nil' channel 会向一个nil channel发送数据阻塞
nilChan <- 1
// 运行错误同上
// 编译器提醒 Receive may block because of 'nil' channel 从nil channel获取数据阻塞
<- nilChan
// panic: close of nil channel 也不能进行关闭会报错 panic
close(nilChan)
}
// 读关闭空零(nil channel)
func readClose(){
// 读关闭空零 读关闭的int->0 string->"" struct->{}
chnClose := make(chan int, 1)
close(chnClose)
d := <- chnClose
fmt.Println(d)
}
// 写关闭异常(nil channel)
func writeClose(){
chnClose := make(chan int, 1)
close(chnClose)
// panic: send on closed channel
chnClose <- 1
fmt.Println(chnClose)
}
// 判断chan是否关闭
func isCloseChan() {
c := make(chan int, 10)
c <- 1
c <- 2
c <- 3
close(c)
for {
i, ok := <-c
fmt.Println(ok)
if !ok {
fmt.Println("channel closed!")
break
}
fmt.Println(i)
}
}
func main() {
nilChanReadWrite()
}
3.对
active/open
状态的
channel
进行测试
active/open
channel
package main
import (
"fmt"
)
// active(open) channel
// 非缓冲chan对于单个读写(只有读操作或者只有写操作)两端的goroutine都会阻塞(deadlock!),只有对一个非缓冲channel同时有读写操作时才不会阻塞(边读边写,读写速度不一样也可)。
// 缓冲chan在容量为空时,读端goroutine会阻塞(deadlock!);在容量未满时,读写两端都不会阻塞;但是容量满了之后,写端会阻塞
// 无缓冲读
func ReadNoDataFromNoBufCh() {
noBufCh := make(chan int)
<-noBufCh
fmt.Println("read from no buffer channel success")
// Output:
// fatal error: all goroutines are asleep - deadlock!
}
// 无缓冲写
func WriteNoBufCh() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch)
fmt.Println("write success no block")
// Output:
// fatal error: all goroutines are asleep - deadlock!
}
// 有缓冲写
func WriteWithBufCh() {
ch := make(chan int, 1)
ch <- 1
// 对已经满了的channel再写入会报错
//ch <- 1
fmt.Println("write success no block")
// Output:
// fatal error: all goroutines are asleep - deadlock!
}
// 有缓冲读
func ReadWithBufCh() {
ch := make(chan int, 5)
ch <- 1
ch <- 1
fmt.Println(<- ch)
fmt.Println("write success no block")
// Output:
// fatal error: all goroutines are asleep - deadlock!
}
// 带缓冲/不带缓冲 阻塞循环读写
func BufReadWrite() {
// 只要读写端同时有操作,不管是否有缓冲都不会阻塞
//ch := make(chan int, 10)
ch := make(chan int, 5)
go func() {
for i := 0; i < 10; i++ {
ch <- i
fmt.Println("写了一个")
}
}()
for i := 0; i < 10; i++ {
fmt.Println(<-ch)
fmt.Println("读了一个")
}
}
// select用法
func ChanSelect() {
ch1 := make(chan int, 5)
ch2 := make(chan int, 5)
ch3 := make(chan int, 5)
ch1 <- 1
ch2 <- 2
ch3 <- 3
select {
case <- ch1:
fmt.Println("shoudao chn1")
case <- ch2:
fmt.Println("shoudao chn222")
case <- ch3:
fmt.Println("shoudao chn3333")
case <-ch2:
default:
fmt.Println("de")
}
}
func main() {
BufReadWrite()
}
4.总结图(借用网上小伙伴 )
版权声明:本文为bigdaddy_maybe原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。