即使使用指针变量周围的互斥锁,如何消除数据竞争

How to eliminate Data Race even with Mutex Locks around pointer variable

这里有一些入门代码,

func (chm *ConcurrentHashMap) NFetchWorker() {
    for {
        key := <-NFetchWorkerPipe
        chm.mu.RLock()
        data := chm.data[string(key)]
        chm.mu.RUnlock()
        if data.IsUsingNFetch {
            chm.mu.Lock()
            *(chm.data[string(key)].NFetch)--
            chm.mu.Unlock()
        }
    }
}

go NFetchWorker()

Struct ConcurrentHashMap 看起来像这样,

type ConcurrentHashMap struct {
    data map[string]DataBlock
    mu sync.RWMutex
}

结构数据块看起来像这样,

type DataBlock struct {
    ...
    NFetch        *int32
    IsUsingNFetch bool
    ...
}

现在,当我尝试 运行 启用比赛标志的测试时。我明白了,

Write at 0x00c00012e310 by goroutine 8:
  (*ConcurrentHashMap).NFetchWorker()

行号指向这一行。

*(chm.data[string(key)].NFetch)--

然而,当我在 DataBlock.NFetch 中不使用指针时,我不会遇到这个问题。 但是,如果我这样做,我将失去直接从 map 对 NFetch 进行更改的能力,而无需将一个全新的结构对象重新分配给 map 中的该哈希,这在计算上会相对昂贵。我想更改 NFetch 的值,而无需为一个小更改再次重新分配整个结构,同时不受 DATA RACE 的影响。有什么解决办法吗??

刚接触 Golang,我可能在这里做了一些非常愚蠢的事情,请随时指出。

更新:添加读写操作函数

func (chm *ConcurrentHashMap) Get(key Key) Value {
    chm.mu.RLock()
    fetch, ok := chm.data[string(key)]
    chm.mu.RUnlock()
    if ok {
        if CheckValueValidity(&fetch) {
            NFetchWorkerPipe <- key
            return fetch.Value
        } else {
            chm.mu.Lock()
            delete(chm.data, string(key))
            chm.mu.Unlock()
        }
    }

    return constants.NIL
}

写操作

func (chm *ConcurrentHashMap) Insert(key Key, value Value, options Options) {
    ...
    chm.mu.Lock()
    chm.data[string(key)] = Block{
        Value:         value,
        NFetch:        nFetchOld,
        Expiry:        time.Now().Add(delay),
        IsUsingNFetch: foundNFetch,
        IsUsingExpiry: foundTTL,
        mu:            mutex,
    }
    chm.mu.Unlock()
}

问题出在 CheckValueValidity 中,您在没有锁定的情况下访问 NFetch。指针不应该在没有锁定的情况下被访问。

func CheckValueValidity(value *Block) bool {
    if value.IsUsingExpiry && !value.Expiry.After(time.Now()) {
        return false
    }

    if value.IsUsingNFetch && *(value.NFetch) <= 0 {
        return false
    }

    return true
}

此代码应该有效

func (chm *ConcurrentHashMap) Get(key Key) Value {
    chm.mu.RLock()
    fetch, ok := chm.data[string(key)]
    isValid := CheckValueValidity(&fetch)
    chm.mu.RUnlock()
    if ok {
        if isValid {
            NFetchWorkerPipe <- key
            return fetch.Value
        } else {
            chm.mu.Lock()
            delete(chm.data, string(key))
            chm.mu.Unlock()
        }
    }

    return constants.NIL
}