Go中的select和channel配合使用,通过select可以监听多个channel的I/O读写事件,当 IO操作发生时,触发相应的动作。
基本使用
// 常规示例
func example() {
done := make(chan interface{})
// 一段时间后发送关闭信号
go func() {
time.Sleep(5 * time.Second)
close(done)
}()
workCounter := 0
breakLoop := false
for {
select {
case <-done:
breakLoop = true
default:
}
if breakLoop {
break
}
workCounter++
time.Sleep(1 * time.Second)
}
fmt.Printf("收到结束信号,任务执行了 %d 次", workCounter)
}
超时机制
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
quit := make(chan bool)
//新开一个协程
go func() {
for {
select {
case num := <-ch:
fmt.Println("num = ", num)
case <-time.After(3 * time.Second):
fmt.Println("超时")
quit <- true
}
}
}()
for i := 0; i < 5; i++ {
ch <- i
time.Sleep(time.Second)
}
// 收到超时信号,停止阻塞
<-quit
fmt.Println("程序结束")
}
最快返回
多个 goroutine 做同一件工作,取最快的返回结果
package main
import (
"fmt"
"github.com/kirinlabs/HttpRequest"
)
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
ch3 := make(chan int)
go Getdata("https://www.baidu.com",ch1)
go Getdata("https://www.baidu.com",ch2)
go Getdata("https://www.baidu.com",ch3)
select{
case v:=<- ch1:
fmt.Println(v)
case v:=<- ch2:
fmt.Println(v)
case v:=<- ch3:
fmt.Println(v)
}
}
func Getdata(url string,ch chan int){
req,err := HttpRequest.Get(url)
if err != nil{
}else{
ch <- req.StatusCode()
}
}
死锁与默认情况
package main
func main() {
ch := make(chan string)
select {
case <-ch:
}
}
在第 4 行创建了一个信道 ch。我们在 select 内部(第 6 行),试图读取信道 ch。由于没有 Go 协程向该信道写入数据,因此 select 语句会一直阻塞,导致死锁。该程序会触发运行时 panic
空select
package main
func main() {
select {}
}
除非有 case 执行,select 语句就会一直阻塞着。在这里,select 语句没有任何 case,因此它会一直阻塞,导致死锁。该程序会触发 panic