我有一个递归的类似地图的结构,如下所示:
type RecurseTable struct {
Table map[string]*RecurseTable
// Other fields
sync.RWMutex
}
如果我要从多个goroutines访问这个结构,我究竟该如何锁定它呢?假设我正在从顶层地图中读取并写入第三级嵌套地图。是否准确地说这不会导致任何问题,因为改变第三级(因此指向两个指针)不应该影响顶级地图?
类似地,如果我在第二级嵌套结构中有一个goroutine池都修改信息,那么我只需要锁定每个二级映射,因为顶级映射只包含指向嵌套的RecurseTable?或者是我必须锁定顶级映射以及嵌套结构,因为结构可能以某种方式在内存中重新分配,导致更改指针存储为顶级映射中的值?
另一种情况是在从二级结构读取时向顶级地图添加键。是否可以安全地假设由于新键而导致的顶级映射的重组不会影响内存中二级结构的位置,因此在读取时没有必要锁定结构? / p>
我的目标是最小化整个递归结构的全局锁,以便我的goroutine可以并行处理结构的不同部分,并且锁争用最小。我想我的问题的核心是关于Golang中的地图如何调整大小。
答案 0 :(得分:2)
来自Go maps in action博文:
映射对于并发使用是不安全的:它没有定义当您同时读取和写入时会发生什么。如果您需要从同时执行的goroutine中读取和写入映射,则访问必须由某种同步机制调解。保护地图的一种常见方法是使用
sync.RWMutex
。
当您将地图指针提供给其他地图时,您可以安全地独立于其他地图(兄弟姐妹或后代)锁定地图:即使您在使用其后代时删除地图条目,也不会有问题,因为它们的引用将被保留,直到你释放指针(即删除它们的var)。
答案 1 :(得分:2)
一种方法是不导出Table成员并提供适当锁定的Get / Set方法。例如:
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
}
这样,访问地图数据的唯一方法是通过保护地图访问权限的方法。然后每个值都被独立保护。