chan 的使用
chan 即 channel,不过它更像 java 的 queue,先进先出,这里,简单介绍下 chan 的相关使用。
chan 与缓冲
所谓缓冲,其实就是 chan 的容量,如果容量是 1,那么写入 1 个数据后,chan 就满了,如果这个数据没被读取走,再次写入时会阻塞;如果没设置容量或容量设置为 0,除非有 goroutines 在读取数据,否则写入时直接阻塞。
这里 myChan := make(chan int)
等价于 myChan := make(chan int, 0)
chan 即使无缓冲(size 为 0)也可以使用,但必须要有协程在读取,否则直接死锁,如果有缓冲,即使暂无协程读取,也可以写入,比如 size 设置为 10,然后写入 10 个数字,写入完成后再读取。
这里,如果 size 设置小于 10,则死锁,缓冲的作用名如其人,主要是为了避免写入到 chan 时阻塞;
chan 的读取与写入
chan 的读取很简单,比如 i := <- myChan
但这可能读取到错误的值,考虑如下代码:
写入 0-8 后,关闭 chan,但读取 20 个,会发现前 9 个是 0-8,后面 11 个全是 0;一般的,读取 chan 建议用
i, ok := <- myChan
这里,如果 ok 为 false 说明 chan 关闭;
使用 for-range 读取 chan
使用 for-range 读取 chan 时,可以不用管 chan 是否已关闭,如果关闭 for 循环会结束,如果没关闭,但无数据,则会阻塞。
使用 select 来读 chan
select 类似 linux io 读写的 select ,即:多路复用。
|
|
这里,select 会监听多个事件,任何一个发生都会触发,一般用于超时,比如读取 5 秒,没数据则返回,或写入但 5 秒内没写入成功,则告警;使用 select 读取时,要警惕 chan close,如果 close ,然后读取,会快速返回而不是阻塞,一般的,并不建议直接 close chan,应该用 context 来确定是否退出,举个例子:
|
|
使用 select 来写入 chan
select 写入 chan 时,如果 chan 满了,则不会执行,比如:
前 2 个输出为 true,后面均为 false,不会阻塞,不分场景,比如一些无关紧要的数据,如果可以丢失,可以考虑用 select 来写入。
chan 总结
- chan 可以指定缓冲区大小,关闭后读取不会 panic,但关闭后写入会 panic;
- 可以使用 for-range 来读取 chan,不用去关心 chan 是否关闭的问题;
- 如果要非阻塞的写入,考虑用 select;
- 如果 chan 没有 goroutines 持有,则 go 垃圾回收会回收内存,不建议显示 close,建议用 context;
- chan 先进先出,但如果有多个 goroutines 读取时,仍需要考虑并发问题;
默认触发的 ticker
考虑如下代码:
由于 for 循环第一个是初始化,第二个是条件,第 3 个是 for 循环执行完成后执行,以上代码可实现默认触发;