go 匿名函数协程中直接修改上级切片的安全性

问题遇到的现象和发生背景

我想确认一下下列代码是否会有问题

问题相关代码,请勿粘贴截图
type bb struct {
    a int
    b string
}
func a1() {
    aa :=[]bb{{a: 0,b: "b0",},{a:1,b:"b1"},{a:2,b:"b2"},{a:3,b:"b3"},{a:4,b:"b4"}}
    for i := 0; i < 5; i++ {
        go func(wz int) {
            aa[wz].b="完毕"+strconv.Itoa(wz)
        }(i)
    }
time.Sleep(time.Millisecond*5000)
    fmt.Printf("最终结果%+v",aa)
}

运行结果及报错内容

最终结果符合预期

我的解答思路和尝试过的方法

目前看来结果达到我的预期

我想要达到的结果

因为项目中我不想用chan来通讯所以想用上述带的思路.
我想知道在实际项目中这样用是否会有问题,实际中bb struct结构体比较复杂

从代码逻辑来看,没毛病,并且结果也是与你的预期一致的:

  1. 由于这里是用的传值匿名函数,go func(wz int)(i),所有没有多协程问题。
  2. 后面的sleep 5秒很关键,但是如果等待的时间不够,上面的协程执行的内容如果比较复杂时,耗时比较多的话,结果可能就会和预期的不同。
  3. 如果aa切片数据比较多时,就会启动非常多的协程。

最好是将sleep 5秒改进一下,用一个Group锁会好得多。

func a1() {
    var wg sync.WaitGroup // 添加定义
    aa := []bb{{a: 0, b: "b0"}, {a: 1, b: "b1"}, {a: 2, b: "b2"}, {a: 3, b: "b3"}, {a: 4, b: "b4"}}

    wg.Add(len(aa)) // 添加计数
    for i := 0; i < 5; i++ {
        go func(wz int) {
            aa[wz].b = "完毕" + strconv.Itoa(wz)
            wg.Done() // 完成一个计数
        }(i)
    }

    wg.Wait() // time.Sleep(time.Millisecond * 5000)
    fmt.Printf("最终结果%+v", aa)
}

你这样做有什么意义吗?这样不会造成死锁、异常错误,但是有什么意义呢?

切片并发的安全性,我觉得主要问题出在:切片容量的扩展、重置、删除等方面,单独修改值不会出现。

不符合切片的意义了

只要测试结果能达到你要的效果,就是正确的。

用信号量来控制协程的结束比sleep更精确,如果协和执行慢,或者快,sleep都是一个估计值。