web-dev-qa-db-fra.com

Carte avec accès simultané

Lorsque vous utilisez une carte dans un programme avec un accès simultané, est-il nécessaire d'utiliser un mutex dans les fonctions pour read values?

57
user1243746

Plusieurs lecteurs, aucun auteur, ça va:

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

Un auteur, pas de lecteurs, ça va. (Les cartes ne seraient pas très bonnes sinon.)

Sinon, s'il existe au moins un écrivain et au moins un écrivain ou un lecteur, les tous lecteurs et auteurs doivent utiliser la synchronisation pour accéder à la carte. Un mutex fonctionne bien pour cela.

86
Sonia

sync.Map a fusionné avec Go master le 27 avril 2017.

C'est la carte concurrente que nous attendions tous. 

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

https://godoc.org/sync#Map

44
Diegomontoya

J'ai répondu à votre question dans this reddit thread il y a quelques jours:

En Go, les cartes ne sont pas thread-safe. De plus, les données doivent être verrouillées même pour lire si, par exemple, il pourrait y avoir une autre goroutine qui est écrire les mêmes données (en même temps).

À en juger par votre précision dans les commentaires, il y aura aussi des fonctions de réglage, la réponse à votre question est oui, vous devrez protéger vos lectures avec un mutex; vous pouvez utiliser un RWMutex . Pour un exemple, vous pouvez regarder le source de la mise en oeuvre d'une structure de données de table (utilise une carte dans les coulisses) que j'ai écrite (en réalité celle liée dans le fil rouge).

21
user11617

Vous pouvez utiliser concurrent-map pour gérer les douleurs liées à la concurrence. 

// 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")
17
orcaman

si vous n'avez qu'un seul auteur, vous pouvez probablement vous en tirer en utilisant une valeur atomique. Ce qui suit est adapté de https://golang.org/pkg/sync/atomic/#example_Value_readMostly (l’original utilise des verrous pour protéger l’écriture, prend en charge plusieurs auteurs)

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.
    }
2
Martyn Weber

Ma simple implémentation:

import (
    "sync"
)

type AtomicMap struct {
    data   map[string]string
    rwLock sync.RWMutex
}

func (self *AtomicMap) Get(key string) (string, bool) {
    self.rwLock.RLock()
    defer self.rwLock.RUnlock()
    val, found := self.data[key]
    return val, found
}

func (self *AtomicMap) Set(key, val string) {
    self.rwLock.Lock()
    defer self.rwLock.Unlock()
    self.data[key] = val
}
0
makiko_fly

Pourquoi ne pas utiliser le modèle de concurrence Go à la place, voici un exemple simple ...

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
}
0
user3215378