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

golang.org/x/sync/syncmap 被 struct 裹挟时 使用前为什么必须为每个键初始化 不然取值得到 nil

  •  
  •   zhouyin · 21 小时 5 分钟前 · 643 次点击
    package main
    
    import (
    	"fmt"
        syncmap "golang.org/x/sync/syncmap"
        
    )
    type Mark struct{
        Values syncmap.Map
    }
    var chanA chan int = make(chan int , 1)
     
    func (m Mark)funa(){
     
        go func(){
            m.Values.Store("a",1)
            chanA<-1
        }()
        
        <-chanA
        
    }
    func main() {
    var mk = new (Mark)
    mk.Values.Store("a",0)
    mk.funa()
    va, p := mk.Values.Load("a")
    fmt.Printf("%v:%v\n",va,p)
    }
    

    没有这行 mk.Values.Store("a",0) 永远取不到值 va 等于 nil p 等于 false

    7 条回复    2025-10-22 11:17:56 +08:00
    zhouyin
        1
    zhouyin  
    OP
       20 小时 53 分钟前
    需要把函数 funa 定义成指针接收者 就不需要初始化
    ```golang
    func (m *Mark)funa(){

    go func(){
    m.Values.Store("a",1)
    chanA<-1
    }()

    <-chanA
    }
    ```
    道理上想不通 值接收者如果设计到变量拷贝 那么在函数里赋值也没用
    buffzty
        2
    buffzty  
       20 小时 19 分钟前
    加个*号
    iseki
        3
    iseki  
       10 小时 57 分钟前 via Android
    > A Map must not be copied after first use.
    SGL
        4
    SGL  
       10 小时 7 分钟前
    原文档内容:The zero Map is empty and ready for use. A Map must not be copied after first use.

    Map 的定义如下:
    ```
    type Map struct {
    _ noCopy // 这个在源码中设置

    m isync.HashTrieMap[any, any]
    }
    ```

    noCopy 类型的说明:
    noCopy may be added to structs which must not be copied
    after the first use.

    See https://golang.org/issues/8005#issuecomment-190753527
    for details.

    Note that it must not be embedded, due to the Lock and Unlock methods.

    如果你想问,进一步为什么 Map 是“noCopy"的,需要进一步研究 HashTrieMap 的结构:

    ```go
    type HashTrieMap[K comparable, V any] struct {
    inited atomic.Uint32
    initMu Mutex
    root atomic.Pointer[indirect[K, V]]
    keyHash hashFunc
    valEqual equalFunc
    seed uintptr
    }
    ```
    可以看到里面封装了 Mutex ,先不管其他变量,至少 Mutex 是肯定不可拷贝的, 内部维护锁的状态( locked/unlocked )以及等待队列这些资源。
    Ketteiron
        5
    Ketteiron  
       8 小时 50 分钟前
    值接收者会强制复制值,这应该知道吧。
    简单来说,sync.Map, sync.Mutex 不能被复制,在你这个用例中,读和写是不同的 map 实例。
    zhouyin
        6
    zhouyin  
    OP
       8 小时 16 分钟前
    @SGL
    对 struct 被拷贝导致 iniMutex 被拷贝了 具体业务代码里 多个 go routine 总共有好几次 Store 就只有一次 Store 的 Key 取出来是 nil 其他 Store 的 key 都有值 说明一开始有两个 goroutine 竞争初始化 导致第一个 goroutine 没 Store 完毕 被第二个 goroutine 初始化整个 map 了
    zhouyin
        7
    zhouyin  
    OP
       8 小时 12 分钟前
    @Ketteiron
    原因是竞争初始化 Mutex 操作的是同一个 map 因为所有 struct 实例持有的都是 map 的指针地址
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   3149 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 11:30 · PVG 19:30 · LAX 04:30 · JFK 07:30
    ♥ Do have faith in what you're doing.