最近在学习go并发和同步相关的知识,关于channel尤其是单缓冲channel有很多疑问,也感谢之前很多大佬的帮助。
在晚上敲了一个关于Once的demo,代码如下
```go
package main
import (
"fmt"
"sync"
)
func main() {
doOnve()
}
func doOnve() {
var once sync.Once
funBody := func() { //被检测的是否只执行1次的函数对象
fmt.Println("Only once")
}
c := make(chan int) //无缓冲channel
for i:=0;i<10;i++{
go func() { //10个协程
once.Do(funBody)
c <- i
}()
}
for i := 0; i < 10; i++ {
fmt.Println( <- c)
}
}
```
运行结果如下![微信截图_20211027204914.png](https://static.studygolang.com/211027/7c6390e369614b94a2e0cfac25e5d2a3.png)
Once的作用体现出来了但是实在不理解为什么会打印出10个10呢(特别是我的i在循环中都没遍历到10)?有没有大佬能帮小白解释一下,感激不尽!
你的测试结果都是一样, 里面有一个点:for里的i是逃逸到堆上的,因此每个goroutine里实际是引用的一个地址值(这个地址值会变化的,它的最后变化值是10), 在多核(多p)情况下,可能会有其它值的输出
#6
更多评论
1、for 循环中的临时变量使用的为相同的一个地址空间、因此for循环完毕时临时变量的值已改变、导致协程中传递到通道的值也会不符合预期。
2、在for循环中起协程使用临时变量需要再次将临时变量的值拷贝一份及赋值到新的变量空间。
3、去除once的执行、会看到会有不同的数字输出、因为for循环和协程是异步的、又因为1的原因、所以值不确定。
4、加上once的执行,每次协程都会先once判断一次才将数据传递到通道,for循环已遍历完毕,因此临时变量的值为最后一值。
5、for循环临时变量会在执行加1操作后,在判断条件、因此临时变量值为10。
#1
老哥 我懂了 我忽略了闭包是同地址的参数
改成下面这样完美解决
```go
for i:=0;i<10;i++{
go func(x int) {
once.Do(funBody)
//c <- i //若是这样写 输出结果会是 10 10 10...10 因为 闭包参数是外面的10 ;正确方法是为该匿名函数设置参数 并按参数输出
c <- x
}(i)
}
```
![image.png](https://static.studygolang.com/211028/d7d4fc854aec5b3415454f14d2b53093.png)
#2