前言:
channel 提供了一种通信机制,通过它,一个 goroutine 可以想另一 goroutine 发送消息。channel 本身还需关联了一个类型,也就是 channel 可以发送数据的类型。例如: 发送 int 类型消息的 channel 写作 chan int 。channel 很强大,但是还是有点坑,下面是我个人的总结。
1.使用两个值接收 channel:
<- channel 能够返回一个值或者 两个值,当返回两个值的时候 ,第二个是一个bool 变量,如果为 false ,就说明这个 channel 被关闭了。被关闭了的 channel ,会不断的 返回 该 channle 类型的 零值 和false,应该在代码 层面做好预防。
unc main () {
can := make(chan int ,8)
can <- 1
can <- 1
can <- 1
close(can)
for {
select {
case tem := <- can:
fmt.Println(tem)
time.Sleep(time.Second)
}
}
}
就比如 这段代码,当can 被读取完管道里面的数据同时是关闭状态的时候,并不会像我们预期的想法那样阻塞住,反而不断的 返回零值。。
func main () {
can := make(chan int ,8)
can <- 1
can <- 1
can <- 1
close(can)
for {
select {
case tem ,fla:= <- can:
if fla == false {
//can = nil 如果这样的 select 就不会一直返回了,
// select 的 一个 nil的 channle就会阻塞住
return
} else {
fmt.Println(tem)
}
time.Sleep(time.Second)
}
}
}
改成 这样我们就能完美 解决该问题了。
2.使用for range 代替 select:
select 能够同时监听 多个channle的 返回值,但是很多时候我们只是一个协程 监听一个 channle,这个时候我们不如 用for range。
func main () {
can := make(chan int ,8)
can <- 1
can <- 1
can <- 1
close(can)
/* for {
select {
case tem ,fla:= <- can:
if fla == false {
//can = nil 如果这样的 select 就不会一直返回了,
//select 的 一个 nil的 channle就会阻塞住
return
} else {
fmt.Println(tem)
}
time.Sleep(time.Second)
}
}
*/
for i := range can {
fmt.Println(i)
}
}
当 for range 一个 channle ,如果 该 channle没有被关闭,会一直阻塞,如果 关闭了,会自动退出循环,很方便。代码简洁了很多!
3.channle不仅仅是队列:
channle 分为有缓存 和无缓存类型。
- 有缓存 channle 是一个消费队列。
- 无缓冲的channle 也可以当锁用。
- channle 因为能够 缓冲数据,所以还很适合 当连接池来用。
具体怎么实现,大家自己可以网上找找资料。
总结:
以上就是我对channle的认识了,有啥不对的,欢迎留言