【问题标题】:Map with concurrent access具有并发访问的映射
【发布时间】:2012-06-19 06:42:00
【问题描述】:

当您在具有并发访问的程序中使用映射时,是否需要在函数中使用互斥体来读取值?

【问题讨论】:

  • 如果严格来说是只读映射,则不需要互斥体。
  • 我不是很清楚,因为会有设置和获取值的函数。

标签: map go mutex


【解决方案1】:

多个读者,没有作家是可以的:

https://groups.google.com/d/msg/golang-nuts/HpLWnGTp-n8/hyUYmnWJqiQJ

一个作家,没有读者是好的。 (否则地图不会好到哪里去。)

否则,如果至少有一位写入者和至少一位写入者或读取者,则所有读取者写入者必须使用同步来访问地图。互斥体可以很好地解决这个问题。

【讨论】:

  • 多个作者,没有读者怎么办?这会导致内存损坏吗?
  • @user3125693 当然。
【解决方案2】:

sync.Map 已于 2017 年 4 月 27 日合并为 Go master。

这是我们一直在等待的并发 Map。

https://github.com/golang/go/blob/master/src/sync/map.go

https://godoc.org/sync#Map

【讨论】:

  • 不错的一个。请注意,新的 sync.Map 类型是为仅附加地图设计的(因此它不使用键分片,这意味着如果您的地图有很多流失,它很可能会低于分片样式地图我上面的回答)。
  • @OmarIlias 我的意思是您主要设置新值(不编辑或删除现有值)的情况。看到这个:github.com/golang/go/issues/20360
  • @orcaman current go sync.Map 可以比散列分片更快或更慢,即使在仅附加的情况下也是如此。这真的取决于场景。使用尽可能多的原子操作,sync.Map 内部可以比传统的分片快得多,因为与传统的哈希桶相比,它最大限度地减少了锁定,但需要锁定。我相信会有基准测试详细说明新的 sync.Map 实现的甜蜜点和痛点。但是假设速度较慢是不正确的,尤其是考虑并发性。
  • @Diegomontoya 当然,这里是:medium.com/@deckarep/…,简而言之,只有当使用的核心数大于 4 时,sync.map 才会更快,如果不是这样,只使用 mutext 会最大速度提高 4 倍
【解决方案3】:

我几天前在thisreddit 帖子中回答了你的问题:

在 Go 中,映射不是线程安全的。此外,数据甚至需要锁定 例如,如果可能有另一个 goroutine 是 写入相同的数据(即并发)。

从您在 cmets 中的说明来看,也会有 setter 函数,您的问题的答案是肯定的,您必须使用互斥锁来保护您的读取;您可以使用RWMutex。举个例子,你可以看看source我写的表数据结构的实现(在后台使用了一个地图)(实际上是在reddit线程中链接的那个)。

【讨论】:

  • 对像地图一样快速访问的资源使用完整的读写锁通常是浪费
  • 你能详细说明一下吗?什么更适合?
  • RW 锁适用于有很多争用的资源,但它们比互斥锁有更多的开销。 Map get/sets 足够快,以至于程序可能没有足够的争用来使更复杂的锁比简单的互斥锁提供更好的吞吐量。
  • 感谢您的澄清。关于这个问题,你有什么论文/文章可以推荐吗?
  • 然而,这篇文章blog.golang.org/go-maps-in-action的并发部分建议使用sync.RWMutex
【解决方案4】:

您可以使用concurrent-map 为您处理并发问题。

// Create a new map.
map := cmap.NewConcurrentMap()

// Add item to map, adds "bar" under key "foo"
map.Add("foo", "bar")

// Retrieve item from map.
tmp, ok := map.Get("foo")

// Checks if item exists
if ok == true {
    // Map stores items as interface{}, hence we'll have to cast.
    bar := tmp.(string)
}

// Removes item under key "foo"
map.Remove("foo")

【讨论】:

  • 像这样的事情就是为什么我不能认真对待“不需要泛型”的概念。
  • 这个概念并不是说 Go“不需要泛型”,而是“目前没有干净的方法来实现泛型,我们需要再考虑一下”。例如,C++ 为正在使用的所有可能的类型组合生成代码,这会不合理地增加编译时间和可执行文件大小。
【解决方案5】:

如果你只有一个作家,那么你可能会使用原子值。以下改编自https://golang.org/pkg/sync/atomic/#example_Value_readMostly(原文使用锁保护写,所以支持多写者)

type Map map[string]string
    var m Value
    m.Store(make(Map))

read := func(key string) (val string) { // read from multiple go routines
            m1 := m.Load().(Map)
            return m1[key]
    }

insert := func(key, val string) {  // update from one go routine
            m1 := m.Load().(Map) // load current value of the data structure
            m2 := make(Map)      // create a new map
            for k, v := range m1 {
                    m2[k] = v // copy all data from the current object to the new one
            }
            m2[key] = val // do the update that we need (can delete/add/change)
            m.Store(m2)   // atomically replace the current object with the new one
            // At this point all new readers start working with the new version.
            // The old version will be garbage collected once the existing readers
            // (if any) are done with it.
    }

【讨论】:

    【解决方案6】:

    为什么不使用 Go 并发模型,有一个简单的例子...

    type DataManager struct {
        /** This contain connection to know dataStore **/
        m_dataStores map[string]DataStore
    
        /** That channel is use to access the dataStores map **/
        m_dataStoreChan chan map[string]interface{}
    }
    
    func newDataManager() *DataManager {
        dataManager := new(DataManager)
        dataManager.m_dataStores = make(map[string]DataStore)
        dataManager.m_dataStoreChan = make(chan map[string]interface{}, 0)
        // Concurrency...
        go func() {
            for {
                select {
                case op := <-dataManager.m_dataStoreChan:
                    if op["op"] == "getDataStore" {
                        storeId := op["storeId"].(string)
                        op["store"].(chan DataStore) <- dataManager.m_dataStores[storeId]
                    } else if op["op"] == "getDataStores" {
                        stores := make([]DataStore, 0)
                        for _, store := range dataManager.m_dataStores {
                            stores = append(stores, store)
                        }
                        op["stores"].(chan []DataStore) <- stores
                    } else if op["op"] == "setDataStore" {
                        store := op["store"].(DataStore)
                        dataManager.m_dataStores[store.GetId()] = store
                    } else if op["op"] == "removeDataStore" {
                        storeId := op["storeId"].(string)
                        delete(dataManager.m_dataStores, storeId)
                    }
                }
            }
        }()
    
        return dataManager
    }
    
    /**
     * Access Map functions...
     */
    func (this *DataManager) getDataStore(id string) DataStore {
        arguments := make(map[string]interface{})
        arguments["op"] = "getDataStore"
        arguments["storeId"] = id
        result := make(chan DataStore)
        arguments["store"] = result
        this.m_dataStoreChan <- arguments
        return <-result
    }
    
    func (this *DataManager) getDataStores() []DataStore {
        arguments := make(map[string]interface{})
        arguments["op"] = "getDataStores"
        result := make(chan []DataStore)
        arguments["stores"] = result
        this.m_dataStoreChan <- arguments
        return <-result
    }
    
    func (this *DataManager) setDataStore(store DataStore) {
        arguments := make(map[string]interface{})
        arguments["op"] = "setDataStore"
        arguments["store"] = store
        this.m_dataStoreChan <- arguments
    }
    
    func (this *DataManager) removeDataStore(id string) {
        arguments := make(map[string]interface{})
        arguments["storeId"] = id
        arguments["op"] = "removeDataStore"
        this.m_dataStoreChan <- arguments
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-05
      • 2016-08-26
      • 2014-01-15
      • 1970-01-01
      • 1970-01-01
      • 2019-12-22
      • 2022-08-17
      相关资源
      最近更新 更多