如何让select一直展开_一文掌握 Go 语言 Select 的四大用法

本文作者:appleboy

原文链接:https://blog.wu-boy.com/2019/11/four-tips-with-select-in-golang/

b6a31ac556503a2ef445a9fcb593d232.png

本文带大家认识 Go 语言的 Select 用法,相信大家对于 switch 并不陌生,然而 select 跟 switch 有个共同特性就是都通过 case 的方式来处理,但是 select 跟 switch 处理的事情完全不同,也完全不相容。来看看 switch 有什么特性: 各种类型及型别操作,接口 interface{} 型别判断 variable.(type),重点是会依照 case 顺序依序执行。先看个例子:

package mainvar (    i interface{})func convert(i interface{}) {    switch t := i.(type) {    case int:        println("i is interger", t)    case string:        println("i is string", t)    case float64:        println("i is float64", t)    default:        println("type not found")    }}func main() {    i = 100    convert(i)    i = float64(45.55)    convert(i)    i = "foo"    convert(i)    convert(float32(10.0))}

运行出来的结果如下:

i is interger 100i is float64 +4.555000e+001i is string footype not found

而 select 的特性就不同了,只能接收 channel,否则会出错,而 default 会直接执行,所以没有 default 的 select 就会遇到 blocking,假设没有送 value 进去 Channel 就会造成 panic,底下拿几个实际例子来解说。

Random Select

同一个 channel 在 select 会随机选取,底下看个例子:

package mainimport "fmt"func main() {    ch := make(chan int, 1)    ch 

执行后会发现有时候拿到 random 01 有时候拿到 random 02,这就是 select 的特性之一,case 是随机选取,所以当 select 有两个 channel 以上时,如果同时对全部 channel 送资料,则会随机选取到不同的 Channel。而上面有提到另一个特性『假设没有送 value 进入 Channel 就会造成 panic』,拿上面例子来改:

func main() {    ch := make(chan int, 1)    select {    case 

执行后会发现变成 deadlock,造成 main 主程式爆炸,这时候可以直接用 default 方式解决此问题:

func main() {    ch := make(chan int, 1)    select {    case 

主程式 main 就不会因为读不到 channel value 造成整个程式 deadlock。

Timeout 超时机制

用 select 读取 channle 时,一定会实作超过一定时间后就做其他事情,而不是一直 blocking 在 select 内。底下是简单的例子:

package mainimport (    "fmt"    "time")func main() {    timeout := make(chan bool, 1)    go func() {        time.Sleep(2 * time.Second)        timeout 

建立 timeout channel,让其他地方可以透过 trigger timeout channel 达到让 select 执行结束,也或者有另一个写法是透握 time.After 机制

select {case 

可以注意 time.After 是回传 chan time.Time,所以执行 select 超过一秒时,就会输出 timeout 02。

检查 channel 是否已满

直接来看例子比较快:

package mainimport (    "fmt")func main() {    ch := make(chan int, 1)    ch 

先宣告 buffer size 为 1 的 channel,先丢值把 channel 填满。这时候可以透过 select + default 方式来确保 channel 是否已满,上面例子会输出 channel blocking,我们再把程式改成底下

func main() {    ch := make(chan int, 2)    ch 

把 buffer size 改为 2 后,就可以继续在塞 value 进去 channel 了,这边的 buffer channel 观念可以看之前的文章『用五分钟了解什么是 unbuffered vs buffered channel』[1]

select for loop 用法

如果你有多个 channel 需要读取,而读取是不间断的,就必须使用 for + select 机制来实现,更详细的实作可以参考『15 分钟学习 Go 语言如何处理多个 Channel 通道』

package mainimport (    "fmt"    "time")func main() {    i := 0    ch := make(chan string, 0)    defer func() {        close(ch)    }()    go func() {    LOOP:        for {            time.Sleep(1 * time.Second)            fmt.Println(time.Now().Unix())            i++            select {            case m := 

上面例子可以发现执行后如下:

1574474619157447462015744746211574474622

其实把 default 拿掉也可以达到目的

select {case m := 

当没有值送进来时,就会一直停在 select 区段,所以其实没有 default 也是可以正常运作的,而要结束 for 或 select 都需要透过 break 来结束,但是要在 select 区间直接结束掉 for 回圈,只能使用 break variable 来结束,这边是大家需要注意的地方。

参考资料

[1]

『用五分钟了解什么是 unbuffered vs buffered channel』: https://blog.wu-boy.com/2019/04/understand-unbuffered-vs-buffered-channel-in-five-minutes/


喜欢本文的朋友,欢迎关注“Go语言中文网”:

5296b57426c21393bb16660db3ff07ea.png

Go语言中文网启用微信学习交流群,欢迎加微信:274768166,投稿亦欢迎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值