取消引用指针的类型断言是内存写入吗?

Is type assertion of a dereferenced pointer a memory write in go?

go race detector 以一种对我来说毫无意义的方式抱怨我的代码,但我想 race detector 的作者比我更了解这一点。

我有这个闭包:

func(f *datastore.F) bool {
    a, ok := (*f).(*datastore.T)
    ...
}

我将其作为参数传递给此函数:

func GetFunc(f func(fid *datastore.F) bool) (*datastore.F, bool) {
    kvs.lock.RLock()
    defer kvs.lock.RUnlock()

    for _, v := range kvs.fs {
        if f(v) {
            return v, true
        }
    }
    return nil, false
}

这是另一个 goroutine 的相关部分:

for read := range [chan of datastore.F] {
    s.lock.Lock()
    s.fs[read.Fi()] = &read
    s.lock.Unlock()
}

kvs 是这种类型的实例:

type kvstore struct {
    lock  sync.RWMutex
    fs map[datastore.Fi]*datastore.F
}

datastore.F是一个接口,*datastore.T实现了那个接口。

竞争检测器抱怨闭包和另一个 goroutine 存在数据竞争。另一个 goroutine 写入,闭包读取。考虑到 sync.RWMutex 到位,我看不到这怎么会发生冲突。

取消引用指针的类型断言不会写入 Go 中的变量。

这个代码

for read := range [chan of datastore.F] {
   s.lock.Lock()
   s.fs[read.Fi()] = &read
   s.lock.Unlock()
}

将映射值设置为局部变量的地址read。变量 read 的范围在 for 循环块之外,并且在循环的每次迭代中都会被修改。所有映射值都包含相同的指针,这可能不是您想要的。

闭包通过取消引用映射中的指针来读取变量read。竞争检测器抱怨,因为 reader(闭包)和编写器(for 循环)之间没有同步。

要解决此问题,请在循环内声明一个新变量:

for read := range [chan of datastore.F] {
   read := read  // <-- Add this line
   s.lock.Lock()
   s.fs[read.Fi()] = &read
   s.lock.Unlock()
}

通过此更改,每个映射值都指向一个设置一次的唯一变量。

在 Go 中很少使用指向接口的指针。解决此问题的首选方法是将类型 *datastore.F 的所有使用更改为 datastore.F。此更改消除了跨 goroutine 边界对变量 read 的引用,并消除了不必要的间接级别。