1.单向管道
func writeChan(chanName chan<- int) 通过形参限定函数内部只能向 channel 写入数据
func readChan(chanName <-chan int)通过形参限定函数内部只能从 channel 中读取数据
//单向管道
func readChan(chanName <-chan int) {
<-chanName
}
func writeChan(chanName chan<- int) {
chanName <- 1
}
func TestWriteRead(t *testing.T) {
var mychan = make(chan int, 10)
writeChan(mychan)
readChan(mychan)
}
2. select 监控多个 channel
当一个 channel 可以操作时就会触发相应的 case 分支
//select
func addNumToChan(chanName chan int) {
for {
chanName <- 1
time.Sleep(1*time.Second)
}
}
func TestChanSelect(t *testing.T) {
chan1 := make(chan int, 10)
chan2 := make(chan int, 10)
go addNumToChan(chan1)
go addNumToChan(chan2)
for {
select {
case e := <- chan1 :
fmt.Printf("Get element from chan1: %d\n", e)
case e := <- chan2 :
fmt.Printf("Get element from chan2: %d\n", e)
default:
fmt.Printf("No element in chan1 and chan2.\n")
time.Sleep(time.Second)
}
}
}
结果:
=== RUN TestChanSelect
No element in chan1 and chan2.
Get element from chan2: 1
Get element from chan1: 1
Get element from chan1: 1
Get element from chan2: 1
No element in chan1 and chan2.
No element in chan1 and chan2.
Get element from chan1: 1
Get element from chan2: 1
Get element from chan1: 1
Get element from chan2: 1
No element in chan1 and chan2.
No element in chan1 and chan2.
从结果可以看出:
- 从channel中读取数据时随机的,这是因为select语句的多个case语句的执行顺序是随机的
- select 的case语句读 channel时不会阻塞,尽管管道中没有数据,这是由于case语句编译后调用读取channel时会明确传入不阻塞参数,读不到数据时不会将当前协程加入等待队列,而是直接返回
3. for-range读取数据
通过for-range 可以像遍历数组一样从 channel 中读取数据,当 channel 中没有数据时,会阻塞当前协程,将当前协程加入recvq队列,进入睡眠并等待被写协程唤醒。
//for-range
func TestChanRange(t *testing.T) {
mychan := make(chan int, 10)
for i := 0; i < 10; i++ {
mychan <- i
}
for e := range mychan {
fmt.Printf("Get elemet from chanName: %d\n", e)
}
}
结果:
=== RUN TestChanRange
Get elemet from chanName: 0
Get elemet from chanName: 1
Get elemet from chanName: 2
Get elemet from chanName: 3
Get elemet from chanName: 4
Get elemet from chanName: 5
Get elemet from chanName: 6
Get elemet from chanName: 7
Get elemet from chanName: 8
Get elemet from chanName: 9
fatal error: all goroutines are asleep - deadlock!
从结果可以看出,当channel缓冲区没有数据时,与for-range与读管道时的阻塞处理机制是一样的。使用for的方式读取,若没有数据可读时,和上面出现的结果一致:
for i := 0; i < 11; i++ {
e := <- mychan
fmt.Printf("Get elemet from chanName: %d\n", e)
}