web-dev-qa-db-ja.com

同時アクセスのマップ

同時アクセスのあるプログラムでマップを使用する場合、関数でミューテックスを使用して読み取り値にする必要はありますか?

60
user1243746

複数のリーダー、ライターはいません:

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

ある作家、大丈夫な読者はいません。 (そうでなければ、マップはあまり良くないでしょう。)

それ以外の場合、少なくとも1人のライターと少なくとも1人のライターまたはリーダーが存在する場合、allリーダーライターは、マップにアクセスするために同期を使用する必要があります。ミューテックスはこれに適しています。

91
Sonia

sync.Mapは、2017年4月27日の時点でGoマスターに統合されました。

これは、私たち全員が待ち望んでいた並行マップです。

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

https://godoc.org/sync#Map

46
Diegomontoya

数日前に this redditスレッドであなたの質問に答えました:

Goでは、マップはスレッドセーフではありません。また、たとえば、同じデータを書き込む別のゴルーチンが存在する可能性がある場合(同時に)、データは読み取りでもロックする必要があります。

コメントの説明から判断すると、セッター関数も存在することになるため、質問への回答はイエスです。読み取りをミューテックスで保護する必要があります。 RWMutex を使用できます。例として、私が書いた(実際にはredditスレッドにリンクされたもの)テーブルデータ構造(バックグラウンドでマップを使用)の実装の source を見ることができます。

21
user11617

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

ライターが1人だけの場合は、おそらくアトミック値を使用して回避できます。以下は 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.
    }
2
Martyn Weber

私の簡単な実装:

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

代わりに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
}
0
user3215378