奇怪的channel 的笔记
channel 不仅能够控制数据传输,还可以控制执行流。
1. 关闭的channel从来都不会阻塞
关闭的channel不能传数据但是可以接受数据,例子
```
package main import "fmt" func main() { ch := make(chan bool, 2) ch <- true ch <- true close(ch) for i := 0; i < cap(ch) +1 ; i++ { v, ok := <- ch fmt.Println(v, ok) } }
```
接受的是默认空值。
替代性地,可以用range
```
package main import "fmt" func main() { ch := make(chan bool, 2) ch <- true ch <- true close(ch) for v := range ch { fmt.Println(v) // called twice } }
```
当channel干枯后就会跳出循环流程,这个循环会执行两次。
但是select才是真功夫。
```
package main import ( "fmt" "sync" "time" ) func main() { finish := make(chan bool) var done sync.WaitGroup done.Add(1) go func() { select { case <-time.After(1 * time.Hour): case <-finish: } done.Done() }() t0 := time.Now() finish <- true // send the close signal done.Wait() // wait for the goroutine to stop fmt.Printf("Waited %v for goroutine to stop\n", time.Since(t0)) }
```
在调用done之前是不会真的等一个小时的。
但是这个程序也会有问题。就是finish 不是带缓冲的。如果接受者忘了加上select里面的语句的话,就会阻塞了。(比如开了100个goroutine,但是这送了99次之类的)
利用关闭了的channel是永远可以接受的特性,把代码改成下面这样。
```
package main import ( "fmt" "sync" "time" ) func main() { const n = 100 finish := make(chan bool) var done sync.WaitGroup for i := 0; i < n; i++ { done.Add(1) go func() { select { case <-time.After(1 * time.Hour): case <-finish: } done.Done() }() } t0 := time.Now() close(finish) // closing finish makes it ready to receive done.Wait() // wait for all goroutines to stop fmt.Printf("Waited %v for %d goroutines to stop\n", time.Since(t0), n) }
```
话说这和没有finish通道不是一个效果么?不管了
实际上如果在通道里的东西,你从来不用,应该构造成空的结构体,就像下面的例子。
```
package main import ( "fmt" "sync" "time" ) func main() { finish := make(chan struct{}) var done sync.WaitGroup done.Add(1) go func() { select { case <-time.After(1 * time.Hour): case <-finish: } done.Done() }() t0 := time.Now() close(finish) done.Wait() fmt.Printf("Waited %v for goroutine to stop\n", time.Since(t0)) }
```
说明这个channel不包含东西。
2. nil channel 永远都是阻塞的。
```
package main func main() { var ch chan bool ch <- true // blocks forever }
```
这是一个死锁。
接受的话也是死锁。
```
package main func main() { var ch chan bool <- ch // blocks forever }
```
这个看起来不是很重要,但是可以用来的等待多个通道关闭。
```
// WaitMany waits for a and b to close. func WaitMany(a, b chan bool) { var aclosed, bclosed bool for !aclosed || !bclosed { select { case <-a: aclosed = true case <-b: bclosed = true } } }
```
这个函数能够等待两个通道关闭。