关于 golang 碰到的一个问题!

2021-11-30 09:30:23 +08:00
 sunny1688

直接上代码,请看图: https://pic.baixiongz.com/uploads/2021/11/30/bc91319946394.jpeg

搞不明白为什么 append slice 会 panic ,出现空指针,而且不是必现,运行一段时间才会出现,一般在几个小时内,求大佬解释一下是为什么

3051 次点击
所在节点    问与答
29 条回复
longfxxx
2021-11-30 09:41:47 +08:00
slice 不需要 make 一下吗?
whitehack
2021-11-30 09:42:54 +08:00
共享了那个 list 变量 没加锁
你先加个锁 还有问题再来问
sujin190
2021-11-30 09:43:44 +08:00
你这图和你右边的输出似乎没啥关系吧
mangoDB
2021-11-30 09:44:10 +08:00
slice 不是 thread safe 的。
sujin190
2021-11-30 09:46:28 +08:00
@longfxxx #1 会自动初始化的
@whitehack #2 不加锁并不会 panic ,只是添加的数量不对,估计右边 panic 显然不是左边这个代码能产生的
sadfQED2
2021-11-30 09:49:55 +08:00
@sujin190 你图片看不到。golang 里面不加锁会 panic
不过空指针应该不是加群的问题,检查下是不是并发情况导致没有初始化
driveby
2021-11-30 09:53:35 +08:00
你这不加锁不是已经 panic 了吗。应该就是 slice 没加锁的原因,照 #2 的方式多跑几遍对照一下就知道结论了。
iyear
2021-11-30 09:54:09 +08:00
@mangoDB 那也不会报空指针的错吧
PungentSauce
2021-11-30 09:54:54 +08:00
你这是把内存跑满了吧
sujin190
2021-11-30 09:58:13 +08:00
@sadfQED2 #6 但是实际测试了并不会,不要猜测啊
sunny1688
2021-11-30 10:00:43 +08:00
@PungentSauce 内存没跑满,跑个一会就会出现,不是立马复现
@mangoDB 对,不是线程安全,最终也是 append 的数量不对,但也不应该是空指针
@longfxxx struct 会自动初始化,有零值,可以直接 append
sunny1688
2021-11-30 10:01:46 +08:00
```go
package main

import (
"fmt"
"sync"
"time"
)

type User struct {
email string
orders []*Order
}

type Order struct {
no string
createdAt time.Time
}

func main() {

total := 0
for {
user := &User{email: "xxxx"}
wg := sync.WaitGroup{}
wg.Add(6)
for i := 0; i < 6; i++ {
go func() {
defer wg.Done()
user.orders = append(user.orders, &Order{})
}()
}
wg.Wait()
total += 1
fmt.Println(user, len(user.orders), "total=", total)
time.Sleep(time.Millisecond * 200)
}
}
```

这是代码,大家可以跑一段时间,然后看看会不会出现空指针
imherer
2021-11-30 10:03:47 +08:00
append 后 slice 如果扩容会导致 demo.list 的地址发生变化
sujin190
2021-11-30 10:07:54 +08:00
@imherer #13 然后原地址可能已经被回收,但因协程调度原因此时有协程才刚开始使用原地址进行操作这样么?嗯,极高并发下看起来还真有可能
imherer
2021-11-30 10:09:16 +08:00
@sujin190 是的
mangoDB
2021-11-30 10:10:35 +08:00
@iyear 我认为 slice 的地址会不断发生变化(因为扩容),在「竞争」的背景下,某个协程拿到的地址不一定是有效的。
sujin190
2021-11-30 10:18:56 +08:00
@imherer #15 但是把如果是 c 和 c++的话,原地址被回收只是代表其会被重用于其它内存分配,地址指向的物理内存是不会消失的,所以也就不会出现空指针错误,除非这是一个双重指针,地址回收的时候更新了第二层指针的指向为空

说起来实际使用来看,go 还真是这么设计的,双重指针,只是这样设计似乎效率低了一点,但是好处确实是保证不会突破内存屏障了,上层使用来看确实有些地方还是很让人莫名其妙的
iyear
2021-11-30 10:23:05 +08:00
@mangoDB 很有道理感谢
sunny1688
2021-11-30 10:30:02 +08:00
@imherer @mangoDB 感谢大神,感谢大神,终于解惑了!
sxfscool
2021-11-30 10:43:01 +08:00
先加个锁

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/818927

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX