Golang中channel的demo为什么出错了?求分析原因

Golang中channel的demo为什么出错了?求分析原因 大家好,我是一个刚开始学习Go的新手。关于我的演示代码,我有一个疑问。我无法理解为什么我的输出会是那样。在我看来,输出应该是“1,2,3…9”。有人能帮我解释一下吗?万分感谢。

package main

import (
	"fmt"
	"sync"
)

func main() {
	doOnve()
}

func doOnve() {
	var once sync.Once
	funBody := func() {
		fmt.Println("Only once")
	}

	c := make(chan int)

	for i:=0;i<10;i++{
		go func() {
			once.Do(funBody)
			c <- i
		}()
	}
	for i := 0; i < 10; i++ {
		fmt.Println( <- c)
	}
}

输出:

image


更多关于Golang中channel的demo为什么出错了?求分析原因的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

非常感谢!!! 👍

更多关于Golang中channel的demo为什么出错了?求分析原因的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


非常感谢!!! 👍

Go语言中闭包的陷阱与常见错误 - Calhoun.io 请参考其中关于"在 for 循环中声明的变量是通过引用传递的"这一部分的开始。

TL;DR:始终通过函数参数将数据传递给 goroutine。

您代码中的 goroutine 是闭包,可以“看到”外部函数的变量。通常,在 goroutine 开始打印循环变量的值之前,循环已经结束。此时,循环变量已经设置为最终值。

将值作为适当的函数参数传递可以避免这种情况。

go func(val int) {
		once.Do(funBody)
			c <- val
		}(i)  // 注意这里的 i

这篇文章对此有更详细的解释。)

你的代码问题在于goroutine闭包中捕获了循环变量i,这会导致所有goroutine共享同一个变量引用。同时,sync.Once确保了funBody只执行一次,但channel接收的顺序是不确定的。

以下是修正后的代码:

package main

import (
	"fmt"
	"sync"
)

func main() {
	doOnce()
}

func doOnce() {
	var once sync.Once
	funBody := func() {
		fmt.Println("Only once")
	}

	c := make(chan int)

	for i := 0; i < 10; i++ {
		go func(idx int) {
			once.Do(funBody)
			c <- idx
		}(i) // 传递i的副本
	}
	
	for i := 0; i < 10; i++ {
		fmt.Println(<-c)
	}
}

关键修改:

  1. 将闭包函数改为接受参数idx int,这样每个goroutine会获得i的独立副本
  2. 在goroutine调用时传入当前的i值:(i)
  3. 修复了函数名拼写错误(doOnve改为doOnce

输出特点:

  • "Only once"只会打印一次(由sync.Once保证)
  • 数字0-9会以随机顺序打印(goroutine执行顺序不确定)
  • 所有数字都会打印,不会重复也不会丢失

原代码的问题:

  • 所有goroutine共享同一个i的引用,最终可能都收到循环结束后的值(通常是10)
  • goroutine启动和执行的时机不确定,导致输出结果不可预测

这是典型的Go并发编程中闭包捕获循环变量的常见问题。通过传递参数副本可以确保每个goroutine获得正确的值。

回到顶部