dourui9570 2018-11-14 15:47
浏览 20

如何为Golang中的并发读/写锁定特定的地图索引

I wanted to know if there is a way to lock only an index in a map during concurrent read/write. I am pretty new to Golang and parallelism sorry if the answer is obvious.

func Check(a, b []string) map[string]int {
    var res = make(map[string]int)
    go func() {
        for _, v := range a {
            res[v]++
        }
    }()
    go func() {
        for _, v := range b {
            res[v]++
        }
    }()
    return res
}

Eventually this piece of code is going to panic due to concurrent map Read/Write. So we should add mutex to lock the map.

var m sync.Mutex
go func() {
    for _, v := range a {
        m.Lock()
        res[v]++
        m.Unlock()
    }
}()
go func() {
    for _, v := range b {
        m.Lock()
        res[v]++
        m.Unlock()
    }
}()

But from my understanding m.lock() will lock my whole map? Isn't this too much overhead by locking everything? This bugged me as i thought this piece of code may not be faster than running linearly. Can I possibly lock only the map at map["some key"], so that my second goroutine can still write in map["some other key"]?

  • 写回答

2条回答 默认 最新

  • duanqian6295 2018-11-14 17:16
    关注

    Maps themselves do not take care of locks, therefore any manipulation of them on multiple go routines (or reading while they are being manipulated) will require some for of syncing (e.g., sync.Mutex). There are fancier things you can do though.

    RW Mutex

    You can get a little fancier depending on your use case and use a sync.RWMutex. This will allow concurrent reads while safely blocking for any write. For example:

    package main
    
    import (
        "sync"
        "time"
    )
    
    func main() {
        m := map[int]int{}
        lock := sync.RWMutex{}
    
        go func() {
            // Writer
            for range time.Tick(250 * time.Millisecond) {
                // Notice that this uses Lock and NOT RLock
                lock.Lock()
                m[5]++
                m[6] += 2
                lock.Unlock()
            }
        }()
    
        go func() {
            for range time.Tick(250 * time.Millisecond) {
                lock.RLock()
                println(m[5])
                lock.RUnlock()
            }
        }()
    
        for range time.Tick(250 * time.Millisecond) {
            lock.RLock()
            println(m[6])
            lock.RUnlock()
        }
    }
    

    This does not give you a key by key locking mechanism though.

    sync.Map

    The sync.Map is provided by the standard library and is robust. It has more fine grained locking.

    package main
    
    import (
        "sync"
        "time"
    )
    
    func main() {
        m := sync.Map{}
    
        go func() {
            // Writer
            for range time.Tick(250 * time.Millisecond) {
                value, _ := m.LoadOrStore(5, 0)
                m.Store(5, value.(int)+1)
                value, _ = m.LoadOrStore(6, 0)
                m.Store(6, value.(int)+2)
            }
        }()
    
        go func() {
            for range time.Tick(250 * time.Millisecond) {
                value, _ := m.LoadOrStore(5, 0)
                println(value.(int))
            }
        }()
    
        for range time.Tick(250 * time.Millisecond) {
            value, _ := m.LoadOrStore(6, 0)
            println(value.(int))
        }
    }
    

    Notice that the code doesn't have any mutexes. Also notice that you get to deal with empty interfaces...

    评论

报告相同问题?

悬赏问题

  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)
  • ¥15 下图接收小电路,谁知道原理
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度