下面的代码中,为什么会输出 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)
}
}
1
iBugOne 2023-06-24 19:18:25 +08:00
竞争问题,你在 close(inCh) 之后加上 time.Sleep(5*time.Millisecond) 就好了
|
2
iyear 2023-06-24 19:20:02 +08:00 2
并非稳定 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 了。 |
3
flyqie 2023-06-24 19:24:37 +08:00 via Android
善用-race 参数能帮助你解决很多奇怪的问题。
|
4
eastphoton 2023-06-24 21:01:56 +08:00 1
试了一下-race 参数检测不出这个问题,但是确实属于 race condition
|
5
CarrieBauch OP @iyear 有道理,多谢解答。race condition 真是防不胜防
|
6
flyqie 2023-06-24 21:42:22 +08:00
|
7
vitoliu 2023-06-24 22:20:32 +08:00
最好是通过 context 关闭 channel ,不要直接 close
|
8
yianing 2023-06-24 23:54:35 +08:00
@eastphoton channel 本来就是协程之间传递数据用的,哪里来的 race ?
|
9
wuzhewuyou 2023-06-25 09:21:24 +08:00 1
有点意思,for i := 0; i < 10; i++
这里的 10 换成偶数基本能输出 5 个,换成奇数一般是 4 个 |
10
eastphoton 2023-06-25 11:06:13 +08:00 1
@yianing 没有 data race 但是有 race condition ,这是两个相似但不同的概念。
channel 确实本身是同步原语,单从并发使用 channel 这一行为本身没有问题,所以不应该看作 data race 也确实没有被 race detector 报告, 但是仍然属于 race condition ,因为计算结果不确定而受协程调度顺序影响,不同的事件时序下会产生不同的结果(并不一定有害,也可能是良性的,看是否符合开发者意图) |
11
gogogo1203 2023-06-25 12:15:00 +08:00
只是 goroutines 的执行顺序不是固定的, 我记得以前碰过类似的问题.
|
12
leonshaw 2023-06-25 18:20:11 +08:00
内层没有 select default ,搞不好 main 先读空了就会死锁
|
13
CarrieBauch OP |