关于 sync.Map 的一点疑问

3 天前
 rainbowStay

sync.Map 在删除时如果是 read 中的 key ,那么并不是真实的删除,而是将 entry 中的 p 置换成 nil ,但是 value 直接设置为 nil ,那么怎么区分一个值是被置换成了 nil 还是一开始就设置为 nil 呢?

796 次点击
所在节点    Go 编程语言
5 条回复
Trim21
3 天前
sync.Map.Load 方法有两个返回值
grzhan
3 天前
关键在于 Sync.Map.Store(k, v) ,当你传值为 nil 调用 Store 时,因为 k,v 的类型是 any (也就是 interface{}),在 runtime 也就是结构体 iface ,如果你传入的是 nil ,golang 会用 iface 把这个 nil 包一下,对应的 _type 与 data 应该都是空值(nil),所以你可以在 sync.Map.Store 里面后续的源码里可以看到,value 是可以拿到内存地址的(&value ),因为这个 value 本质上是个 iface 结构体,而直接写 &nil 在 Golang 是会编译报错的。

而 readOnly.m 的值类型是 entry ,entry.p 是一个指向 interface{} 值的指针。当你调用 Sync.Map.Store(k, nil) 时,对应的 entry.p 不会变成 nil ,而是变成一个指向 interface{} (iface) 的指针,这个 iface 相当于包装了值 nil 。

而 Sync.Map.Delete() 就确实会把 entry.p 变成 nil ,所以二者确实是有明确区别的。

写一个函数就可以简单验证,也可以汇编拿出来自己看下:

package main

import "fmt"

func printAnyAddr(v any) {
fmt.Println(&v)
}

func main() {
printAnyAddr(nil)
}
grzhan
3 天前
这里 iface 写错了,interface{} 应该是对应 eface
kingcanfish
2 天前
func main() {

var a interface{}
fmt.Println(a == nil)
a = (*int)(nil)
fmt.Println(a == nil)
}

上面的答案是 true 和 false

OP 要是弄懂了上面的原因 ,那应该就能弄懂你提的问题
rainbowStay
2 天前
@grzhan #2 感谢回复,解答得很清晰

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

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

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

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

© 2021 V2EX