我最近使用 Go 的经验,在审查一些代码时,我发现虽然它被写保护,但读取数据时存在问题。不是读取本身,而是读取和修改 slice 之间可能发生的修改。
type ConcurrentSlice struct {
sync.RWMutex
items []Item
}
type Item struct {
Index int
Value Info
}
type Info struct {
Name string
Labels map[string]string
Failure bool
}
如前所述,写作以这种方式受到保护:
func (cs *ConcurrentSlice) UpdateOrAppend(item ScalingInfo) {
found := false
i := 0
for inList := range cs.Iter() {
if item.Name == inList.Value.Name{
cs.items[i] = item
found = true
}
i++
}
if !found {
cs.Lock()
defer cs.Unlock()
cs.items = append(cs.items, item)
}
}
func (cs *ConcurrentSlice) Iter() <-chan ConcurrentSliceItem {
c := make(chan ConcurrentSliceItem)
f := func() {
cs.Lock()
defer cs.Unlock()
for index, value := range cs.items {
c <- ConcurrentSliceItem{index, value}
}
close(c)
}
go f()
return c
}
但是在收集 slice 的内容和修改它之间,可能会发生修改。可能是另一个例程修改了同一个 slice ,当该赋值时,它已经不存在了:slice[i ] = 项目
处理这个问题的正确方法是什么?
我已经实现了这个方法:
func GetList() *ConcurrentSlice {
if list == nil {
denylist = NewConcurrentSlice()
return denylist
}
return denylist
}
我是这样使用它的:
concurrentSlice := GetList()
concurrentSlice.UpdateOrAppend(item)
但我知道在获取和修改之间,即使它实际上是立即的,另一个例程也可能修改了 slice 。以原子方式执行这两个操作的正确方法是什么?我阅读的 slice 100% 是我修改的 slice 。因为如果我尝试将一个项目分配给一个不再存在的索引,它会中断执行。
提前致谢!
最佳答案
您进行阻止的方式是不正确的,因为它不能确保您退回的元素没有被移除。在更新的情况下,数组仍将至少保持相同的长度。
一个更简单的可行解决方案如下:
func (cs *ConcurrentSlice) UpdateOrAppend(item ScalingInfo) {
found := false
i := 0
cs.Lock()
defer cs.Unlock()
for _, it := range cs.items {
if item.Name == it.Name{
cs.items[i] = it
found = true
}
i++
}
if !found {
cs.items = append(cs.items, item)
}
}
关于go - 在读取和修改之前锁定 slice ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73859360/