Golang 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 < 10; i++ {
inCh <- i
}
close(inCh)
for res := range outCh {
fmt.Println(res)
}
}
Golang Go语言一段代码输出疑惑
更多关于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]
解释:
a
是一个包含三个元素的切片。b
是从a
中切片得到的,包含a
的第二个元素。- 切片
b
共享底层数组a
的内存。 - 因此,当修改
b[0]
时,实际上也修改了a
中对应的元素。 len
函数返回切片的长度,cap
函数返回切片的容量(即底层数组的大小)。
如果你的代码输出与预期不符,请检查是否有类似的切片共享或内存修改问题。如果能提供具体的代码片段,我可以给出更精确的解释。