我有一个结构,MyStruct,它包含一个映射。我想使对地图的访问对并发读写是安全的,但我也希望坚持使用基本的Map,而不是使用sync.Map。
因此,我在MyStruct方法上创建插入、删除和获取方法,这些方法由互斥对象保护。代码如下所示
type MyStruct struct {
mu sync.Mutex
myMap map[string]string
}
func (myStruct *MyStruct) Add(val string) {
myStruct.mu.Lock()
myStruct.myMap[val] = val
myStruct.mu.Unlock()
}
func (myStruct *MyStruct) Remove(val string) {
myStruct.mu.Lock()
delete(myStruct.myMap, val)
myStruct.mu.Unlock()
}
func (myStruct *MyStruct) Fetch(val string) string {
myStruct.mu.Lock()
ret := delete(myStruct.myMap, val)
myStruct.mu.Unlock()
return ret
}到目前一切尚好。
不过,MyStruct的一些客户端也需要遍历myStruct.myMap,这就是我的问题。哪个是最好的设计,使并发安全也循环操作,而不是在MyStruct的方法?目前我看到了两种选择
myMap和MyStruct的互斥myMap公开,并将使循环线程安全的责任转移给客户端。这很简单,但不知何故,MyStruct不太关心它的客户端--。
还有其他的可能性吗?对哪种设计有更好的建议吗?
发布于 2021-05-23 09:12:40
还有sync.Map,它具有您需要的所有特性。主要的缺点是它不使用静态类型(因为Go中缺少泛型)。这意味着您必须在任何地方键入断言才能像使用常规地图一样使用它。老实说,使用sync.Map并用静态类型重新声明所有方法可能是最简单的,这样客户端就不必担心执行类型断言。如果你不喜欢sync.Map,请看我的其他建议。
首先要提到的一个改进是将sync.Mutex替换为sync.RWMutex。这允许同时执行多个读取操作。然后,将Fetch更改为使用mu.RLock()和mu.RUnlock()
用于循环遍历映射
安全地迭代每个值并执行回调(对整个迭代保持锁定)。注意,由于锁定,您不能在回调中调用Delete或Add,因此我们不能在迭代期间修改映射。否则,在迭代期间修改映射是有效的,请参阅此answer的工作方式。
func (myStruct *MyStruct) Range(f func(key, value string)) {
myStruct.mu.RLock()
for key, value := range myStruct.myMap {
f(key, value)
}
myStruct.mu.RUnlock()
}下面是这个用法
mystruct.Range(func(key, value string) {
fmt.Println("map entry", key, "is", value)
})这里是相同的,但是传入带有回调的映射,这样回调函数就可以直接修改映射。此外,在迭代进行修改时,也会更改为正则锁。注意,如果回调保留对映射的引用并将其存储在某个地方,它将有效地破坏您的封装。
func (myStruct *MyStruct) Range(f func(m map[string]string, key, value string)) {
myStruct.mu.Lock()
for key, value := range myStruct.myMap {
f(myStruct.myMap, key, value)
}
myStruct.mu.Unlock()
}这里有一个使用更干净的选项,因为锁定是精心管理的,因此您可以在回调中使用其他锁定函数。
func (myStruct *MyStruct) Range(f func(key, value string)) {
myStruct.mu.RLock()
for key, value := range myStruct.myMap {
myStruct.mu.RUnlock()
f(key, value)
myStruct.mu.RLock()
}
myStruct.mu.RUnlock()
}请注意,range代码执行时始终保持读锁,但在f执行时从未持有读锁。这意味着测距是安全的*,但是回调f可以自由地调用任何其他需要锁定的方法,比如Delete。
脚注:在我看来,选项3的用法是最干净的,但主要要注意的是,由于它在整个迭代过程中不连续保持锁,这意味着任何迭代都可能受到其他并发修改的影响。例如,如果在映射有5个键的情况下开始迭代,并同时执行其他代码删除键,则不能说迭代是否会看到所有5个键。
https://stackoverflow.com/questions/67657750
复制相似问题