it-swarm.com.de

Karte mit gleichzeitigem Zugriff

Wenn Sie eine Karte in einem Programm mit gleichzeitigem Zugriff verwenden, müssen Sie dann einen Mutex in Funktionen für read values ​​verwenden?

57
user1243746

Mehrere Leser, keine Autoren sind in Ordnung:

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

Ein Schriftsteller, kein Leser ist in Ordnung. (Karten wären sonst nicht gut.)

Andernfalls, wenn mindestens ein Schreiber und mindestens ein weiterer Schreiber oder Leser vorhanden ist, müssen alle Leser und Schreiber die Synchronisierung verwenden, um auf die Karte zuzugreifen. Ein Mutex funktioniert dafür gut.

86
Sonia

sync.Map ist seit dem 27. April 2017 zu Go master verschmolzen.

Dies ist die gleichzeitige Map, auf die wir alle gewartet haben. 

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

https://godoc.org/sync#Map

44
Diegomontoya

Ich habe deine Frage vor ein paar Tagen im this reddit Thread beantwortet:

In Go sind Maps nicht threadsicher. Daten müssen auch für .__ gesperrt werden. Lesen, wenn es zum Beispiel eine andere Goroutine gibt, die .__ ist. Schreiben der gleichen Daten (gleichzeitig).

Nach Ihrer Klarstellung in den Kommentaren zu urteilen, dass es auch Setterfunktionen geben wird, ist die Antwort auf Ihre Frage "Ja". Sie müssen Ihre Lesevorgänge mit einem Mutex schützen. Sie können ein RWMutex verwenden. Als ein Beispiel können Sie sich die von source der Implementierung einer Tabellendatenstruktur ansehen (verwendet eine Karte hinter den Kulissen), die ich geschrieben habe (tatsächlich die im reddit-Thread verknüpfte).

21
user11617

Sie können concurrent-map verwenden, um die gleichzeitigen Schmerzen für Sie zu behandeln. 

// 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

wenn Sie nur einen Schreiber haben, können Sie wahrscheinlich einen atomaren Wert verwenden. Folgendes ist angepasst an https://golang.org/pkg/sync/atomic/#example_Value_readMostly (das Original verwendet Sperren, um das Schreiben zu schützen, unterstützt also mehrere Schreiber)

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

Meine einfache Implementierung:

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

Warum kein Go-Parallelitätsmodell stattdessen verwendet wurde, gibt es ein einfaches Beispiel ...

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