Go并发编程之Select(4)
Admin
2022-05-20 21:33:17
GoLang
Go的select功能和C中的select, poll, epoll类似,就是监听 IO 操作,当 IO 操作发生时,触发相应的动作。
在某些场景下,我们需要同时从多个通道接收数据。通道在接收数据时,如果没有数据可以接收将会发生阻塞。
使用 select 类似于 switch 语句,每个case会对应一个通道的通信(接收或发送)过程,但是它的 case 涉及到 channel 有关的 I/O 操作或者说 select 就是用来监听和 channel 有关的 IO 操作,当 IO 操作发生时,触发相应的动作。
// 基本用法 select { case <- ch1: // 如果ch1成功读到数据,则进行该case处理语句 case ch2 <- 1: // 如果成功向ch2写入数据,则进行该case处理语句 default: // 如果上面都没有成功,则进入default处理流程 }
案例1:
package main import "fmt" func main() { c, quit := make(chan int), make(chan int) go func() { c <- 2 // 添加数据 quit <- 1 // 发送完成信号 } () for isQuit := false; !isQuit; { // 监视信道c的数据流出 select { case v := <-c: fmt.Printf("received %d from c", v) case <-quit: isQuit = true // quit信道有输出,关闭for循环 } } }
案例2:
实现timeout机制
package main import ( "fmt" "time" ) func main() { timeOut := make(chan int) ch := make(chan int) go func() { time.Sleep(1e9) timeOut <- 1 }() select { case <-ch: fmt.Println("c is ") case a := <-timeOut: fmt.Println("timeout", a) } }
同时等待读取ch和timeOut,当超时时间有数据时候,<- timeout会执行,即 select 语句会退出。 而不是一直阻塞在 ch 的读取操作上。 从而实现了对 ch 读取操作的超时设置。
如果select包含default,则上述两个都无法成功读取的情况下,直接进入default。
案例3:
package main import ( "fmt" "time" ) func main() { ch1 := make(chan int) go s1(ch1) for { select { case c1 := <- ch1: fmt.Println("c1 is", c1) case <- time.After(time.Second * 2): fmt.Println("timeout: 2s") return } } } func s1(ch chan int) { for i:=0; i<100; i++ { ch <- i time.Sleep(3 * time.Second) } }
switch和select区别:
select可处理一个或多个channel的发送/接收操作。
如果多个case同时满足,select会随机选择一个。
对于没有case的select,会一直等待,可用于阻塞main函数。
switch可以是各种类型,select 只能是IO操作(通常是channel)。
switch是顺序执行的。
switch中的default是默认的意思,当所有case不满足的时候,就会执行default。
select中的default是当select发现没有case满足,要block时的选择。