目录
- 无缓冲channel等价于缓冲大小为0的channel,而不是1
- 发送者和接收者哪些情况会阻塞
- close哪些情况会导致panic
- 如何优雅的关闭channel
- 当一个select中有多个channel满足可读时,谁被激活
- select with default
- 读取时获取第二个返回值,以此判断该channel是否被关闭
- close前写入的数据,接收者依然可以按顺序读取到
- 一个channel有多个接收者时,close channel会唤醒所有接收者
- 配合timer实现channel读取的超时机制
- 当channel只用做同步通知,不关心channel中传输的值时,可使用
chan struct{}
类型 - 单向channel类型的作用
- 可以make单向channel,但是这样做没有意义
- channel配合
for range
的简化写法
1. 无缓冲channel等价于缓冲大小为0的channel,而不是1
1 2 3 4 5 6 7 8 9 10
| var ch chan int
ch := make(chan int)
close(ch)
|
2. 发送者和接收者哪些情况会阻塞
- 往值为nil的channel发送数据: 永久阻塞
- 从值为nil的channel读取数据: 永久阻塞
- 无缓冲模式的发送者: 阻塞直到数据被接收者接收
- 无缓冲模式的接收者: 无数据可读时,阻塞
- 有缓冲模式的发送者: 当缓冲满时,阻塞
- 有缓冲模式的接收者: 无数据可读时,阻塞
3. close哪些情况会导致panic
- close值为nil的channel
- close已经被close的channel
- 向已经被close的channel发送数据
4. 如何优雅的关闭channel
需要特别注意:
- 接收者关闭channel要小心,因为关闭后发送者继续发送会panic
- 当有多个发送者时,在一个发送者协程中关闭channel要小心,因为关闭后其他发送者继续发送会panic
复杂情况下的参考思路:
- channel的关闭并非必须的,只要channel对象不再被持有,垃圾回收器会清理它
- 可使用原子变量等同步原语保证close有且只有发生一次
- 除了传输数据的channel,可以再增加channel配合select使用,用于取消生产、消费
- 接收端也可以通过其他channel发出消息,反向通知发送端
5. 当一个select中有多个channel满足可读时,谁被激活
Go随机选取一个满足条件的case分支执行,而不是按代码顺序选取。
1 2 3 4 5 6 7 8 9 10 11
| ch1 := make(chan int, 8) ch2 := make(chan int, 8) ch1 <- 1 ch2 <- 1 select { case <- ch1: fmt.Println("ch1") case <- ch2: fmt.Println("ch2") }
|
6. select with default
当select中的条件都不满足时,会立即执行default分支
1 2 3 4 5 6 7 8 9 10 11
| ch1 := make(chan int, 1) ch2 := make(chan int) select { case <- ch1: fmt.Println("ch1") case <- ch2: fmt.Println("ch2") default: fmt.Println("default") }
|
7. 读取时获取第二个返回值,以此判断该channel是否被关闭
8. close前写入的数据,接收者依然可以按顺序读取到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ch := make(chan int, 8) ch <- 1 ch <- 2 ch <- 3 close(ch)
for { v, ok := <- ch fmt.Println(v, ok) if !ok { break } }
|
9. 一个channel有多个接收者时,close channel会唤醒所有接收者
10. 配合timer实现channel读取的超时机制
但在高性能场景需要注意,参见: golang源码阅读之定时器以及避坑指南
11. 当channel只用做同步通知,不关心channel中传输的值时,可使用 chan struct{}
类型
好处是语意上更正确,代码可读性更高。
1 2 3 4 5
| ch := make(chan struct{}, 1)
ch <- chan struct{}{}
|
12. 单向channel类型的作用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
type Timer struct { C <-chan Time r runtimeTimer }
func NewTimer(d Duration) *Timer { c := make(chan Time, 1) t := &Timer{ C: c, r: runtimeTimer{ when: when(d), f: sendTime, arg: c, }, } startTimer(&t.r) return t }
func After(d Duration) <-chan Time { return NewTimer(d).C }
|
13. 可以make单向channel,但是这样做没有意义
1 2
| ch := make(chan<- int, 4)
|
14. channel配合for range
的简化写法
1 2 3 4 5 6 7 8 9 10 11 12
| ch := make(chan int, 4) ch <- 1 ch <- 2 close(ch) for v := range ch { fmt.Println(v) } fmt.Println("< for")
|
参考链接
本文完,作者yoko,尊重劳动人民成果,转载请注明原文出处: https://pengrl.com/p/23102/