目录
一、循环接收多个信道的问题
在使用通道时,想同时接收多个通道的数据是一件困难的事情。通道在接收数据时,如果没有数据可以接收将会发生阻塞。
虽然可以使用如下模式进行遍历,但运行性能会非常差。
for{
// 尝试接收ch1通道
data, ok := <-ch1
// 尝试接收ch2通道
data, ok := <-ch2
// 接收后续通道
…
}
二、select详解
Go 语言中提供了 select 关键字,可以同时响应多个通道的操作。select 的每个 case 都会对应一个通道的收发过程。当收发完成时,就会触发 case 中响应的语句。多个操作在每次 select 中挑选一个进行响应。
select{
case 操作1:
响应操作1
case 操作2:
响应操作2
…
default: //可选操作,fallthrough 行为,和普通的 switch 相似,是不允许的。
没有操作情况
}
操 作 |
语句示例 |
接收任意数据 |
case <- ch: |
接收变量 |
case d := <- ch: |
发送数据 |
case ch <- 100: |
select 做的就是:选择处理列出的多个通信情况中的一个,有如下说明:
- 在任何一个 case 中执行 break 或者 return,select 就结束了
- 如果都阻塞了,会等待直到其中一个可以处理
- 如果多个可以处理,随机选择一个
- 如果没有通道操作可以处理并且写了 default 语句,它就会执行:default 永远是可运行的(这就是准备好了,可以执行)。在 select 中使用发送操作并且有 default 可以确保发送不被阻塞!如果没有 default,select 就会一直阻塞
select 语句实现了一种监听模式,通常用在(无限)循环中;在某种情况下,通过 break 语句使循环退出。
//select 通道的多路复用
package main
import (
"fmt"
"time"
)
//往通道中写数据pump1
func pump1(ch chan int){
for i:=1;; i++{
ch <- i*2
}
}
//往通道中写数据pump2
func pump2(ch chan int){
for i:=1; ; i++{
ch <- i+2
}
}
//接收通道中的数据
func suck(ch1, ch2 chan int) {
for{
select{
case v:= <- ch1:
fmt.Println("received on channel 1:",v)
case v2:= <-ch2:
fmt.Println("received on channel 2:", v2)
}
}
}
func main(){
ch1 := make(chan int)
ch2 := make(chan int)
go pump1(ch1)
go pump2(ch2)
go suck(ch1, ch2)
time.Sleep(time.Second)
}
输出:
Received on channel 2: 5
Received on channel 2: 6
Received on channel 1: 0
Received on channel 2: 7
Received on channel 2: 8
Received on channel 2: 9
Received on channel 2: 10
Received on channel 1: 2
Received on channel 2: 11
...
Received on channel 2: 47404
Received on channel 1: 94346
Received on channel 1: 94348
一秒内的输出非常惊人,如果我们给它计数(goroutine_select2.go),得到了 90000 个左右的数字。