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的最初数量