go里面select-case和time.Ticker的使用注意事项

来源:互联网 发布:李世宏dota2 知乎 编辑:程序博客网 时间:2024/05/16 08:46

出处:
https://studygolang.com/articles/5224

上周末参加Go技术聚会,京东的美女工程师讲到一个select-case和time.Ticker的使用注意事项(真实的应用场景是:在测试收包的顺序的时候,加了个tick就发现丢包了),觉得很有意思,记录一下。

package mainimport (    "fmt"    "runtime"    "time")func init() {    runtime.GOMAXPROCS(runtime.NumCPU())}func main() {    ch := make(chan int, 1024)    go func(ch chan int) {        for {            val := <-ch            fmt.Printf("val:%d\n", val)        }    }(ch)    tick := time.NewTicker(1 * time.Second)    for i := 0; i < 20; i++ {        select {        case ch <- i:        case <-tick.C:            fmt.Printf("%d: case <-tick.C\n", i)        }           time.Sleep(200 * time.Millisecond)    }    close(ch)    tick.Stop()}

输出如下:

val:0val:1val:2val:3val:4val:56: case <-tick.Cval:7val:8val:910: case <-tick.Cval:11val:12val:13val:1415: case <-tick.Cval:16val:17val:18val:19

问题出在这个select里面:

select {    case ch <- i:    case <-tick.C:    fmt.Printf("%d: case <-tick.C\n", i)}

当两个case条件都满足的时候,运行时系统会通过一个伪随机的算法决定哪个case将会被执行
所以当tick.C条件满足的那个循环,有某种概率造成ch<-i没有发送(虽然通道两端没有阻塞,满足发送条件)

解决方案1: 一旦tick.C随机的case被随机到,就多执行一次ch<-i (不体面,如果有多个case就不通用了)

select {    case ch <- i:    case <-tick.C:    fmt.Printf("%d: case <-tick.C\n", i)    ch <- i}

解决方案2: 将tick.C的case单独放到一个select里面,并加入一个default(保证不阻塞)

select {    case ch <- i:}select {    case <-tick.C:    fmt.Printf("%d: case <-tick.C\n", i)    default:}

两种解决方案的输出都是希望的结果:

val:0val:1val:2val:3val:45: case <-tick.Cval:5val:6val:7val:8val:910: case <-tick.Cval:10val:11val:12val:13val:1415: case <-tick.Cval:15val:16val:17val:18val:19
阅读全文
0 0
原创粉丝点击