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

wk3368 · · 14877 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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

package main

import (
	"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:0
val:1
val:2
val:3
val:4
val:5
6: case <-tick.C
val:7
val:8
val:9
10: case <-tick.C
val:11
val:12
val:13
val:14
15: case <-tick.C
val:16
val:17
val:18
val: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:0
val:1
val:2
val:3
val:4
5: case <-tick.C
val:5
val:6
val:7
val:8
val:9
10: case <-tick.C
val:10
val:11
val:12
val:13
val:14
15: case <-tick.C
val:15
val:16
val:17
val:18
val:19


版权声明:本文为博主原创文章,未经博主允许不得转载。


有疑问加站长微信联系

本文来自:CSDN博客

感谢作者:wk3368

查看原文:go里面select-case和time.Ticker的使用注意事项

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:1006366459

14877 次点击  ∙  1 赞  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传