Golang Go语言一段代码竞争问题 输出疑惑

发布于 1周前 作者 htzhanglong 最后一次编辑是 5天前 来自 Go语言

下面的代码中,为什么会输出 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 &lt; 10; i++ {
	inCh &lt;- i
}

close(inCh)

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

}


Golang Go语言一段代码输出疑惑

更多关于Golang Go语言一段代码竞争问题 输出疑惑的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

14 回复

竞争问题,你在 close(inCh) 之后加上 time.Sleep(5*time.Millisecond) 就好了

更多关于Golang Go语言一段代码竞争问题 输出疑惑的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


并非稳定 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 了。

善用-race 参数能帮助你解决很多奇怪的问题。

试了一下-race 参数检测不出这个问题,但是确实属于 race condition

有道理,多谢解答。race condition 真是防不胜防

#3

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

看来-race 参数也不能太过信任。

最好是通过 context 关闭 channel ,不要直接 close

channel 本来就是协程之间传递数据用的,哪里来的 race ?

有点意思,for i := 0; i < 10; i++

这里的 10 换成偶数基本能输出 5 个,换成奇数一般是 4 个

没有 data race 但是有 race condition ,这是两个相似但不同的概念。
channel 确实本身是同步原语,单从并发使用 channel 这一行为本身没有问题,所以不应该看作 data race 也确实没有被 race detector 报告,
但是仍然属于 race condition ,因为计算结果不确定而受协程调度顺序影响,不同的事件时序下会产生不同的结果(并不一定有害,也可能是良性的,看是否符合开发者意图)

只是 goroutines 的执行顺序不是固定的, 我记得以前碰过类似的问题.

内层没有 select default ,搞不好 main 先读空了就会死锁



感谢两位大佬的回复。

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

当然,我很乐意帮你解答关于Go语言代码输出的疑惑。不过,由于我无法直接看到你的代码,我将提供一个常见的代码示例和相应的解释,希望这能帮助你理解问题所在。

假设你有以下Go语言代码:

package main

import "fmt"

func main() {
    a := []int{1, 2, 3}
    b := a[1:2]
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(len(a), cap(a))
    fmt.Println(len(b), cap(b))
    b[0] = 10
    fmt.Println(a)
    fmt.Println(b)
}

这段代码会输出:

[1 2 3]
[2]
3 3
1 2
[1 10 3]
[10]

解释:

  1. a 是一个包含三个元素的切片。
  2. b 是从 a 中切片得到的,包含 a 的第二个元素。
  3. 切片 b 共享底层数组 a 的内存。
  4. 因此,当修改 b[0] 时,实际上也修改了 a 中对应的元素。
  5. len 函数返回切片的长度,cap 函数返回切片的容量(即底层数组的大小)。

如果你的代码输出与预期不符,请检查是否有类似的切片共享或内存修改问题。如果能提供具体的代码片段,我可以给出更精确的解释。

回到顶部