channel通道笔记
介绍
-
语法
- 1.一般使用
make创建channel(常用)c := make(chan datatype),datatype是数据类型
- 2.直接显示声明,创建的值为空,一般没有太大意义
var c chan datatype
- 1.一般使用
-
三种定义写法:
- 既可以收数据又可以发数据:
chan datatype - 只可以收数据:
chan <- datatype - 只可以发数据:
<- chan datatype
- 既可以收数据又可以发数据:
-
用法
- 将数据发送到通道:
channel变量 <- 数据,eg:c <- 10 - 将通道数据发出:
接收变量名:= <- channel变量,egdata := <- c - 将数据丢掉:
<- channel变量,不接收数据
- 将数据发送到通道:
-
注意
- 通道一次只能接收一次数据,等到通道内部数据被接收后,再接收下一个数据
-
类型
- 介绍:通道分为有缓冲和无缓冲的通道,Go提供内置函数
len和cap,无缓冲的通道的len和cap都是0,有缓冲的len表示没有被读取的元素数,cap代表整个通道的容量 - 作用
- 无缓冲:通信和两个
goroutine的同步(没有缓冲,一旦接收就要有人来收数据不然就报错) - 有缓冲:主要通信(因为有缓冲所以可以保存数据,意义是一接收到数据可以没有人收(不报错))
- 无缓冲:通信和两个
- 语法:
- 无缓冲:
make(chan datatype) - 有缓冲:
make(chan datatype,len),创建缓冲为len的通道
- 无缓冲:
- 介绍:通道分为有缓冲和无缓冲的通道,Go提供内置函数
-
实现
goroutine之间的同步等待- 代码实现
func main() {/创建无缓冲通道c := make(chan struct{})//通道要做的事情go func(i chan struct{}) {sum := 0for i := 0; i < 10000; i++ {sum += i}println(sum)//数据写进通道c <- struct{}{}}(c)println(runtime.NumGoroutine())//读通道c,通过通道进行同步等待<-c//丢掉数据}写到缓冲通道的数据不会消失,加上这一点我们可以实现用无缓冲同步
goroutines,用有缓冲存储数据- 代码演示
func main() {c := make(chan struct{})ci := make(chan int, 100)go func(i chan struct{}, j chan int) {for i := 0; i < 10; i++ {ci <- i}close(ci)//写通道c <- struct{}{}}(c, ci)fmt.Printf("----------\n")println(runtime.NumGoroutine())//读通道c,通过通道进行同步等待<-c//此时ci通道已经关闭,匿名函数启动的goroutine已经退出fmt.Printf("----------\n")println(runtime.NumGoroutine())//但通道ci还可以继续读取fmt.Printf("----------\n")for v := range ci {println(v)}}详细解读:
1.创建两个通道 c 和 ci,其中 c 是一个无缓冲通道,用于协程的同步等待;ci 是一个带有缓冲区大小为 100 的通道,用于向协程传递整数。
2.启动一个匿名函数作为一个新的协程,并将 c 和 ci 作为参数传递给它。
3.协程中的 for 循环会向 ci 通道中写入 0 到 9 的整数,然后通过调用 close(ci) 来关闭通道。
4.在协程的最后,向 c 通道写入一个空结构体,以通知主协程协程已经完成。
5.在主协程中,调用runtime.NumGoroutine()打印当前的goroutine 数量,此时应该只有主协程和一个新的协程。
6.等待协程完成,通过 <-c 读取 c 通道中的值,阻塞主协程,直到协程完成。
7.执行runtime.NumGoroutine(),此时应该只有主协程,新的协程已经退出。
8.使用for v := range ci循环读取ci通道中的值,由于通道已经关闭,因此循环会在所有值都被读取后结束。
9.在循环中,使用 println(v) 打印从 ci 通道中读取的每个整数。 -
补充
- 接收通道数据
- 1.使用
range接收:for v := range ci{}//ci为通道数据 - 2.使用
if value,ok := ci,通过判断ok来判断数据是否结束
- 1.使用
- 接收通道数据
-
操作不同状态触发的三种行为
- 触发
panic- 1.向已经关闭的通道写入数据(关闭通道应该由写入者关闭)
- 2.重复关闭的通道
- 阻塞
- 1.向未初始化的通道写入数据或读取数据导致当前
goroutine永久阻塞 - 2.向缓冲区已满的通道写入数据会导致
goroutine阻塞 - 3.通道中没有数据,读取该通道会导致
goroutine阻塞
- 1.向未初始化的通道写入数据或读取数据导致当前
- 非阻塞
- 1.读取已经关闭的通道不会引发阻塞,而是立即返回通道元素类型的零值
- 2.向有缓冲且没有满的通道读/写不会引发阻塞
- 触发
2.利用循环取有缓存的通道数据
- 取数据需要用
for range - 取数据前需要将通道关闭:
close(channel) - 如果用
for循环取数据,不能写成for i:=0;i<len(channel);i++,因为len的大小会随着去除数据而变小,解决方案: 提前用num保存channel的最初数量