概述
此篇文章介绍 channel 的用法
- 无缓冲 channel
- 缓冲 channel
- channel的关闭特点
- range channel
- select channel
每一种,配上完整的代码及相应的测试结果,对关键的部分,配置上图及对应说明。
实践
无缓冲 channel
未分配空间的 channel 具有 阻塞的功能。交互的 goroutine 两都都会阻塞的效果。
无缓充的 channel
总结如下:
- 第1步,两个 goroutine 都到达通道,但都没有开始执行发送或接收
- 第2步,左侧的 goroutine 将手伸进了通道,模拟了向通道发送数据的行为。此时,这个 goroutine 会在通道中被锁住,直道交换完成。
- 第3步,右侧 goroutine 将手放入通道,模拟了从通道里接收数据。这个 goroutine 一样也会在通道中被锁住,直到交换完成
- 第4步与第5步,进行交换。并最终,在第6步,两个 goroutine 都将手从通道里拿出来,模拟了被锁住的 goroutine 得到释放。
代码
package main
import "fmt"
func main() {
// 定义一个 channel,并没有分配空间
c := make(chan int)
// 匿名函数
go func() {
defer fmt.Println("goroutine调用结束...")
fmt.Println("goroutine 正在运行...")
c <- 666
}()
num := <-c
fmt.Println("num:=", num)
fmt.Println("main goroutine 结束。。。")
}
结果
执行结果如下:
缓冲 channel
- 第1步,右侧的 goroutine 正在从通道接收一个值
- 第2步,右侧的这个 goroutine
独立
完成了接收值
的动作,而左侧的 goroutine 正在发送一个新值至通道里 - 第3步,左侧的 goroutine 还在向通道发送新值,而右侧的 goroutine 正在从通道接收另外一个值。这两个步骤里的操作既不是同步的,也不会相互阻塞。
- 第4步,所有的发送和接收都完成,而通道里还有几个值,也有一些空间可以存更多的值。
特点:当 channel 已经满,再向里面写数据,就会阻塞,当 channel 为空时,从里面取数据也会阻塞。
代码
package main
import (
"fmt"
"time"
)
func main() {
// 带有缓冲的 channel
c := make(chan int, 3)
fmt.Println("len(c)= ", len(c), " ,cap(c)=", cap(c))
go func() {
defer fmt.Println("子goroutine执行结束...")
for i := 0; i < 4; i++ {
c <- i
fmt.Println("子goroutine正在运行,发送的元素=", i, "len(c)= ", len(c), " ,cap(c)=", cap(c))
}
}()
time.Sleep(2 * time.Second)
for i := 0; i < 4; i++ {
num := <-c
fmt.Println("num=", num)
}
fmt.Println("main 结束...")
}
结果
执行结果如下:
channel的关闭特点
- channel 不像文件一样需要经常关闭,只有确实没有任何发送数据了,或者想显式的结束 range 循环之类的,才去关闭 channel
- 关闭 channel 后,无法向 channel 再发送数据(引发 panic 错误后导致接收立即返回零值)
- 关闭 channel 后,可以继续从 channel 接收数据
- 对于 nil channel ,无论收发都会被阻塞
代码
package main
import "fmt"
func main() {
c := make(chan int)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
// close可以关闭一个 channel
close(c)
}()
for {
// ok 如果为true表示channel没有关闭,如果为false表示channel已经关闭
if data, ok := <-c; ok {
fmt.Println(data)
} else {
break
}
}
fmt.Println("main finished...")
}
结果
执行结果如下:
range代码
range 写法,完整代码如下
package main
import "fmt"
func main() {
c := make(chan int)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
// close可以关闭一个 channel
close(c)
}()
// 可以使用 range 来迭代不断操作 channel
for data := range c {
fmt.Println(data)
}
}
结果
range-channel 测试结果如下
select channel
代码
package main
import "fmt"
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 5; i++ {
fmt.Println(<-c)
}
// close可以关闭一个 channel
quit <- 0
}()
x, y := 1, 1
for {
select {
case c <- x:
// 如果 c 可写,则该 case 会进来
x = y
y = x + y
case <-quit:
fmt.Println("quit")
return
}
}
}
结果
结束
Golang channel的
基本定义及使用 至此结束,如有疑问,欢迎评论区留言。