for - Mapa con acceso concurrente
golang atomic counter (6)
Cuando utiliza un mapa en un programa con acceso concurrente, ¿hay alguna necesidad de usar un mutex en las funciones para leer los valores?
Múltiples lectores, sin escritores está bien:
https://groups.google.com/d/msg/golang-nuts/HpLWnGTp-n8/hyUYmnWJqiQJ
Un escritor, ningún lector está bien. (De lo contrario, los mapas no serían muy buenos).
De lo contrario, si hay al menos un escritor y al menos un escritor o lector, todos los lectores y escritores deben usar la sincronización para acceder al mapa. Un mutex funciona bien para esto.
Por qué no se utilizó el modelo de simultaneidad Go en su lugar, hay un ejemplo 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
}
Puede usar concurrent-map para manejar los problemas de concurrencia por usted.
// 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")
Respondí tu pregunta en this hilo de reddit hace unos días:
En Go, los mapas no son seguros para subprocesos. Además, los datos requieren bloqueo incluso para la lectura si, por ejemplo, podría haber otra rutina que está escribiendo los mismos datos (al mismo tiempo, eso es).
A juzgar por su aclaración en los comentarios, que también habrá funciones de colocador, la respuesta a su pregunta es sí, tendrá que proteger sus lecturas con un mutex; puedes usar un RWMutex . Por ejemplo, puede ver el source de la implementación de una estructura de datos de tabla (utiliza un mapa detrás de escena) que escribí (en realidad, el que está vinculado en el hilo de reddit).
si solo tienes un escritor, entonces probablemente puedas usar el valor atómico. Lo siguiente está adaptado de https://golang.org/pkg/sync/atomic/#example_Value_readMostly (el original usa bloqueos para proteger la escritura, por lo que admite varios escritores)
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.
}
sync.Map
ha fusionado con Go master a partir del 27 de abril de 2017.
Este es el mapa concurrente que todos hemos estado esperando.