锁定golang递归映射

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

Locking golang recursive map

问题

我有一个类似于递归映射的结构,看起来像这样:

type RecurseTable struct {
    Table map[string]*RecurseTable
    // 其他字段
    sync.RWMutex
}

如果我要从多个goroutine访问这个结构,我应该如何进行锁定?假设我从顶层映射中读取,并向第三层嵌套映射中写入。可以准确地说,这不会引起任何问题,因为更改第三层(从而间接通过两个指针进行间接引用)不会影响顶层映射吗?

类似地,如果我有一个goroutine池,它们都在修改第二层嵌套结构中的信息,那么我只需要锁定每个第二层映射,因为顶层映射只包含指向嵌套RecurseTable的指针吗?还是说我必须同时锁定顶层映射和嵌套结构,因为结构体可能在内存中重新分配,从而导致在顶层映射中存储的指针值发生变化?

另一种情况是在读取第二层结构时向顶层映射添加键。可以安全地假设由于新键而对顶层映射进行的任何重新组织都不会影响第二层结构在内存中的位置,因此在读取时不需要锁定结构体吗?

我的目标是尽量减少对整个递归结构的全局锁定,以便我的goroutine可以并行地处理结构的不同部分,减少锁争用。我想问的核心问题是关于Golang中映射如何调整大小的。

英文:

I have a recursive map-like structure which looks like this:

type RecurseTable struct {
    Table map[string]*RecurseTable
    // Other fields
    sync.RWMutex
}

If I am going to access this structure from multiple goroutines, how exactly do I go about locking it? Let's say I'm reading from the top-level map and writing to the third-level, nested map. Is it accurate to say that this wouldn't cause any problems because changing the third level (and hence indirecting through two pointers) shouldn't affect the top level map?

Similarly, if I have a pool of goroutines all modifying information in the second-level, nested struct, would it be the case I only need to lock each second-level map because the top-level map only contains a pointer to the nested RecurseTable? Or is it the case that I have to lock both the top-level map as well as the nested struct, because the struct could somehow be reallocated in memory, causing a change to the pointer stored as a value in the top-level map?

Another situation would be adding keys to the top-level map whilst reading from the second-level struct. Is it safe to assume that any reorganising of the top-level map due to the new key wouldn't affect the location of the second-level struct in memory, and hence it wouldn't be necessary to lock the struct whilst reading?

My goal here is to minimise global locks of the entire recursive structure so that my goroutines can work on different parts of the structure in parallel, with minimal lock contention. I guess the core of my question is about how maps in Golang resize.

答案1

得分: 2

Go maps in action博文中:

Maps are not safe for concurrent use:当你同时读取和写入映射时,未定义会发生什么。如果你需要在并发执行的goroutine中从映射中读取和写入数据,访问必须通过某种同步机制进行调节。保护映射的一种常见方式是使用sync.RWMutex

由于你正在将映射指针提供给其他映射,所以你可以独立地锁定一个映射,而不受其他映射(兄弟或后代)的影响:即使在使用其后代时删除了映射条目,也不会有问题,因为它们的引用将保留,直到你释放指针(即删除持有它们的变量)。

英文:

From the Go maps in action blogpost :

> Maps are not safe for concurrent use: it's not defined what happens when you read and write to them simultaneously. If you need to read from and write to a map from concurrently executing goroutines, the accesses must be mediated by some kind of synchronization mechanism. One common way to protect maps is with sync.RWMutex.

As you are feeding the map pointers to other maps, you can safely lock a map independently from the others (siblings or descendants): even if you delete a map entry while using its descendants, it won't be a problem since their references will be kept until you release the pointers (ie the var holding them is deleted).

答案2

得分: 2

一种方法是不导出Table成员,并提供适当的获取/设置方法进行加锁。例如:

type RecurseTable struct {
    table map[string]*RecurseTable
    // 其他字段
    sync.RWMutex
}

func New() *RecurseTable {
    return &RecurseTable{
        table: make(map[string]*RecurseTable),
    }
}

func (r *RecurseTable) Get(key string) (*RecurseTable, bool) {
    r.RLock()
    defer r.RUnlock()
    return r.table[key]
}

func (r *RecurseTable) Set(key string, value *RecurseTable) {
    r.Lock()
    defer r.Unlock()
    r.table[key] = value
}

这样,访问map的唯一方式是通过保护对map的访问的方法。然后,每个值都独立地受到保护。

英文:

One approach would be to not export the Table member and provide Get/Set methods that lock appropriately. For example:

type RecurseTable struct {
    table map[string]*RecurseTable
    // Other fields
    sync.RWMutex
}

func New() *RecurseTable {
    return &RecurseTable{
    	table: make(map[string]*RecurseTable),
    }
}

func (r *RecurseTable) Get(key string) (*RecurseTable, bool) {
	r.RLock()
	defer r.RUnlock()
	return r.table[key]
}

func (r *RecurseTable) Set(key string, value *RecurseTable) {
	r.Lock()
	defer r.Unlock()
	r.table[key] = value
}  

This way the only way to access the map's data is via the methods which guard access to the map. Each value is then guarded independently.

huangapple
  • 本文由 发表于 2014年8月5日 15:13:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/25133116.html
匿名

发表评论

匿名网友

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

确定