关于 Golang Go语言中 sync.Map 的一点疑问

发布于 1周前 作者 sinazl 来自 Go语言

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


关于 Golang Go语言中 sync.Map 的一点疑问
6 回复

sync.Map.Load 方法有两个返回值

更多关于关于 Golang Go语言中 sync.Map 的一点疑问的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


关键在于 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)
}

这里 iface 写错了,interface{} 应该是对应 eface

func main() {

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

上面的答案是 true 和 false

OP 要是弄懂了上面的原因 ,那应该就能弄懂你提的问题

#2 感谢回复,解答得很清晰

关于您在 Golang 中对 sync.Map 的疑问,我很乐意提供一些专业的解答。

sync.Map 是 Go 语言标准库提供的一个并发安全的 map 实现,它适用于读多写少的场景。与传统的 map 相比,sync.Map 通过内部机制减少了锁的使用,从而提高了并发性能。

在使用 sync.Map 时,您可能会遇到以下几个常见操作:

  1. Store:用于存储键值对。如果键已经存在,则更新其对应的值。
  2. Load:根据键加载值。如果键存在,则返回对应的值及其是否存在的标志;如果键不存在,则返回 nil 和 false。
  3. LoadOrStore:如果键存在,则返回其对应的值及其是否存在的标志;如果键不存在,则存储键值对并返回存储的值及 true。
  4. Delete:根据键删除键值对。
  5. Range:遍历 sync.Map 中的所有键值对,对每个键值对执行指定的函数。

需要注意的是,sync.Map 不支持像传统 map 那样的索引操作,即不能通过键直接获取值(除非使用 LoadLoadOrStore 方法)。此外,由于 sync.Map 的内部实现较为复杂,因此在某些情况下,其性能可能不如使用互斥锁(sync.Mutex)保护的 map

希望这些信息能帮助您更好地理解和使用 sync.Map。如果您有其他问题或需要更详细的解答,请随时提问。

回到顶部