一段 Go 代码输出疑惑

2023-06-24 18:55:10 +08:00
 CarrieBauch

下面的代码中,为什么会输出 5,6,7,8,9 啊?难道不是应该输出 6,7,8,9 吗?

package main

import "log"

func NewRingBuffer(inCh, outCh chan int) *ringBuffer {
	return &ringBuffer{
		inCh:  inCh,
		outCh: outCh,
	}
}

type ringBuffer struct {
	inCh  chan int
	outCh chan int
}

func (r *ringBuffer) Run() {
	for v := range r.inCh {
		select {
		case r.outCh <- v:
		default:
			<-r.outCh // pop one item from outchan
			r.outCh <- v
		}
	}
	close(r.outCh)
}

func main() {
	inCh := make(chan int)
	outCh := make(chan int, 4)
	rb := NewRingBuffer(inCh, outCh)
	go rb.Run()

	for i := 0; i < 10; i++ {
		inCh <- i
	}

	close(inCh)

	for res := range outCh {
		fmt.Println(res)
	}

}


2456 次点击
所在节点    Go 编程语言
13 条回复
iBugOne
2023-06-24 19:18:25 +08:00
竞争问题,你在 close(inCh) 之后加上 time.Sleep(5*time.Millisecond) 就好了
iyear
2023-06-24 19:20:02 +08:00
并非稳定 5,6,7,8,9 的,我跑了一下也有 6,7,8,9 出现。在 close(inCh) 下方 sleep 就稳定复现 6,7,8,9 了。


for i := 0; i < 10; i++ {
inCh <- i
}
这段代码结束时存在一种可能,Run 协程在 for v := range r.inCh 接收了 9 ,然后切换到 main 开始遍历 outCh(此时 9 并未开始入队),会把里面的 5 读出来,这时就会产生 5,6,7,8,9 。如果 sleep ,就会切到 Run 协程,则 5 就会被 9 顶掉,就 6,7,8,9 了。
flyqie
2023-06-24 19:24:37 +08:00
善用-race 参数能帮助你解决很多奇怪的问题。
eastphoton
2023-06-24 21:01:56 +08:00
试了一下-race 参数检测不出这个问题,但是确实属于 race condition
CarrieBauch
2023-06-24 21:15:58 +08:00
@iyear 有道理,多谢解答。race condition 真是防不胜防
flyqie
2023-06-24 21:42:22 +08:00
@eastphoton #3

多谢指点,手机端发帖看到 race condition 就下意识发了。

看来-race 参数也不能太过信任。
vitoliu
2023-06-24 22:20:32 +08:00
最好是通过 context 关闭 channel ,不要直接 close
yianing
2023-06-24 23:54:35 +08:00
@eastphoton channel 本来就是协程之间传递数据用的,哪里来的 race ?
wuzhewuyou
2023-06-25 09:21:24 +08:00
有点意思,for i := 0; i < 10; i++

这里的 10 换成偶数基本能输出 5 个,换成奇数一般是 4 个
eastphoton
2023-06-25 11:06:13 +08:00
@yianing 没有 data race 但是有 race condition ,这是两个相似但不同的概念。
channel 确实本身是同步原语,单从并发使用 channel 这一行为本身没有问题,所以不应该看作 data race 也确实没有被 race detector 报告,
但是仍然属于 race condition ,因为计算结果不确定而受协程调度顺序影响,不同的事件时序下会产生不同的结果(并不一定有害,也可能是良性的,看是否符合开发者意图)
gogogo1203
2023-06-25 12:15:00 +08:00
只是 goroutines 的执行顺序不是固定的, 我记得以前碰过类似的问题.
leonshaw
2023-06-25 18:20:11 +08:00
内层没有 select default ,搞不好 main 先读空了就会死锁
CarrieBauch
2023-06-26 11:05:08 +08:00
@eastphoton @iyear

感谢两位大佬的回复。

再次打扰一下。针对这类型的问题,有没有什么书籍或者文章系统性的介绍吗?想去查漏补缺一下,补齐知识的盲点

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/951250

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX