V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
wangnimabenma
V2EX  ›  问与答

使用了 sync 的 map 在做自增操作的时候还有必要上锁吗

  •  
  •   wangnimabenma · 2022-04-10 21:11:57 +08:00 · 838 次点击
    这是一个创建于 748 天前的主题,其中的信息可能已经有所发展或是发生改变。
    
        a, _ := monitor.ProgressBars[configName].Progress.Load("number")
        monitor.ProgressBars[configName].Progress.Store("number", a.(int)+1)
        
    

    我的理解里,map 只能保证并发读写,但是保证不了 先读后写的自增,需要上锁

     m.Lock()
       a, _ := monitor.ProgressBars[configName].Progress.Load("number")
        monitor.ProgressBars[configName].Progress.Store("number", a.(int)+1)
     m.Unlock()
    

    不知道哪种是正解,求前辈指点

    acehowxx
        1
    acehowxx  
       2022-04-10 21:36:04 +08:00 via Android
    我觉得不用加锁。已经都用 sync.map 了还加啥锁。
    wangnimabenma
        2
    wangnimabenma  
    OP
       2022-04-10 21:37:57 +08:00
    @acehowxx #1 刚开始我也感觉不用加锁,但是有个情况没搞清楚。 Load 和 Store 是原子的, +1 操作是非原子的
    acehowxx
        3
    acehowxx  
       2022-04-10 21:55:08 +08:00 via Android
    @wangnimabenma 那个 store 是个函数,你+1 后就已经是那个函数的第二个参数了。而且你取出来的 a 也是 map 里值的副本,你改变 a 的值并不会改 map 里的值。
    cloverstd
        4
    cloverstd  
       2022-04-10 22:37:36 +08:00
    不加锁是 goroutine 安全的,但不是业务并发安全
    number 应该存 *int64 ,然后用 atomic 去自增
    wangnimabenma
        5
    wangnimabenma  
    OP
       2022-04-10 22:51:09 +08:00
    wangnimabenma
        6
    wangnimabenma  
    OP
       2022-04-10 22:53:16 +08:00
    @acehowxx #3 刚测试了下,sync map 的自增是不能并发安全的
    ```go
    m := sync.Map{}
    m.Store("test", 0)

    go func() {
    for true {
    c,_ := m.Load("test")
    m.Store("test", c.(int)+1)
    time.Sleep(1 * time.Second)
    fmt.Println(c)
    }
    }()

    go func() {
    for true {
    c,_ := m.Load("test")
    m.Store("test", c.(int)+1)
    time.Sleep(1 * time.Second)
    fmt.Println(c)
    }
    }()
    go func() {
    for true {
    c,_ := m.Load("test")
    m.Store("test", c.(int)+1)
    time.Sleep(1 * time.Second)
    fmt.Println(c)
    }
    }()
    go func() {
    for true {
    c,_ := m.Load("test")
    m.Store("test", c.(int)+1)
    time.Sleep(1 * time.Second)
    fmt.Println(c)
    }
    }()

    time.Sleep(30 * time.Second)
    ```
    wangnimabenma
        7
    wangnimabenma  
    OP
       2022-04-10 23:01:42 +08:00
    @cloverstd #4 感谢,测试了下是这样的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1298 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 17:39 · PVG 01:39 · LAX 10:39 · JFK 13:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.