具有并发访问的地图

huangapple go评论82阅读模式
英文:

Map with concurrent access

问题

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

英文:

When you use a map in a program with concurrent access, is there any need to use a mutex in functions to read values?

答案1

得分: 116

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

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

一个写者,没有读者也是可以的。(否则,map就没有太大用处了。)

否则,如果至少有一个写者和至少一个更多的写者或读者,那么所有读者和写者必须使用同步来访问map。互斥锁对此很有效。

英文:

Multiple readers, no writers is okay:

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

One writer, no readers is okay. (Maps wouldn't be much good otherwise.)

Otherwise, if there is at least one writer and at least one more either writer or reader, then all readers and writers must use synchronization to access the map. A mutex works fine for this.

答案2

得分: 66

sync.Map已于2017年4月27日合并到Go主分支中。

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

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

https://godoc.org/sync#Map

英文:

sync.Map has merged to Go master as of April 27, 2017.

This is the concurrent Map we have all been waiting for.

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

https://godoc.org/sync#Map

答案3

得分: 25

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

> 在Go中,映射是不安全的。此外,即使只是读取数据,如果可能有另一个goroutine正在写入相同的数据(并发地),数据也需要进行锁定。

根据你在评论中的澄清,如果还会有setter函数,那么对于读取操作,你需要使用互斥锁来保护;你可以使用RWMutex。你可以查看我编写的一个表数据结构(在幕后使用映射)的源代码作为示例(实际上是Reddit帖子中链接的那个)。

英文:

I answered your question in this reddit thread few days ago:

> In Go, maps are not thread-safe. Also, data requires locking even for
> reading if, for example, there could be another goroutine that is
> writing the same data (concurrently, that is).

Judging by your clarification in the comments, that there are going to be setter functions too, the answer to your question is yes, you will have to protect your reads with a mutex; you can use a RWMutex. For an example you can look at the source of the implementation of a table data structure (uses a map behind the scenes) which I wrote (actually the one linked in the reddit thread).

答案4

得分: 23

你可以使用concurrent-map来处理并发问题。

// 创建一个新的map。
map := cmap.NewConcurrentMap()

// 向map中添加项,在键“foo”下添加“bar”
map.Add("foo", "bar")

// 从map中检索项。
tmp, ok := map.Get("foo")

// 检查项是否存在
if ok == true {
    // Map将项存储为interface{},因此我们需要进行类型转换。
    bar := tmp.(string)
}

// 删除键“foo”下的项
map.Remove("foo")
英文:

You could use concurrent-map to handle the concurrency pains for you.

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

答案5

得分: 3

如果您只有一个写入者,那么您可能可以使用原子值来解决问题。以下代码改编自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) { // 从多个Go协程中读取
    m1 := m.Load().(Map)
    return m1[key]
}

insert := func(key, val string) {  // 从一个Go协程中更新
    m1 := m.Load().(Map) // 加载当前数据结构的值
    m2 := make(Map)      // 创建一个新的映射
    for k, v := range m1 {
        m2[k] = v // 将当前对象的所有数据复制到新对象中
    }
    m2[key] = val // 进行所需的更新(可以删除/添加/更改)
    m.Store(m2)   // 原子地用新对象替换当前对象
    // 此时,所有新的读取器开始使用新版本。
    // 旧版本将在现有读取器(如果有)完成后进行垃圾回收。
}
英文:

if you only have one writer, then you can probably get away with using an atomic Value. The following is adapted from https://golang.org/pkg/sync/atomic/#example_Value_readMostly (the original uses locks to protect writing, so supports multiple writers)

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

得分: 0

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

type DataManager struct {
    /** 这个变量包含了连接到数据存储的信息 **/
    m_dataStores map[string]DataStore

    /** 这个通道用于访问数据存储的映射 **/
    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)
    // 并发...
    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
}

/**
 * 访问映射函数...
 */
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
}
英文:

Why no made use of Go concurrency model instead, there is a simple example...

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 := &lt;-dataManager.m_dataStoreChan:
				if op[&quot;op&quot;] == &quot;getDataStore&quot; {
					storeId := op[&quot;storeId&quot;].(string)
					op[&quot;store&quot;].(chan DataStore) &lt;- dataManager.m_dataStores[storeId]
				} else if op[&quot;op&quot;] == &quot;getDataStores&quot; {
					stores := make([]DataStore, 0)
					for _, store := range dataManager.m_dataStores {
						stores = append(stores, store)
					}
					op[&quot;stores&quot;].(chan []DataStore) &lt;- stores
				} else if op[&quot;op&quot;] == &quot;setDataStore&quot; {
					store := op[&quot;store&quot;].(DataStore)
					dataManager.m_dataStores[store.GetId()] = store
				} else if op[&quot;op&quot;] == &quot;removeDataStore&quot; {
					storeId := op[&quot;storeId&quot;].(string)
					delete(dataManager.m_dataStores, storeId)
				}
			}
		}
	}()
	
	return dataManager
}

/**
 * Access Map functions...
 */
func (this *DataManager) getDataStore(id string) DataStore {
	arguments := make(map[string]interface{})
	arguments[&quot;op&quot;] = &quot;getDataStore&quot;
	arguments[&quot;storeId&quot;] = id
	result := make(chan DataStore)
	arguments[&quot;store&quot;] = result
	this.m_dataStoreChan &lt;- arguments
	return &lt;-result
}

func (this *DataManager) getDataStores() []DataStore {
	arguments := make(map[string]interface{})
	arguments[&quot;op&quot;] = &quot;getDataStores&quot;
	result := make(chan []DataStore)
	arguments[&quot;stores&quot;] = result
	this.m_dataStoreChan &lt;- arguments
	return &lt;-result
}

func (this *DataManager) setDataStore(store DataStore) {
	arguments := make(map[string]interface{})
	arguments[&quot;op&quot;] = &quot;setDataStore&quot;
	arguments[&quot;store&quot;] = store
	this.m_dataStoreChan &lt;- arguments
}

func (this *DataManager) removeDataStore(id string) {
	arguments := make(map[string]interface{})
	arguments[&quot;storeId&quot;] = id
	arguments[&quot;op&quot;] = &quot;removeDataStore&quot;
	this.m_dataStoreChan &lt;- arguments
}

huangapple
  • 本文由 发表于 2012年6月16日 20:35:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/11063473.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定