V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
rainbowStay
V2EX  ›  Go 编程语言

关于 sync.Map 的一点疑问

  •  
  •   rainbowStay · 18 天前 · 1161 次点击

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

    5 条回复    2024-11-05 19:29:28 +08:00
    Trim21
        1
    Trim21  
       18 天前
    sync.Map.Load 方法有两个返回值
    grzhan
        2
    grzhan  
       18 天前   ❤️ 5
    关键在于 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
    grzhan  
       18 天前
    这里 iface 写错了,interface{} 应该是对应 eface
    kingcanfish
        4
    kingcanfish  
       18 天前   ❤️ 1
    func main() {

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

    上面的答案是 true 和 false

    OP 要是弄懂了上面的原因 ,那应该就能弄懂你提的问题
    rainbowStay
        5
    rainbowStay  
    OP
       18 天前
    @grzhan #2 感谢回复,解答得很清晰
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2651 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 11:52 · PVG 19:52 · LAX 03:52 · JFK 06:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.