如何在指针变量周围使用互斥锁来消除数据竞争。

huangapple go评论64阅读模式
英文:

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()

ConcurrentHashMap 结构体如下,

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

DataBlock 结构体如下,

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

现在当我尝试使用 race 标志运行测试时,我得到以下警告,

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

行号指向以下代码行,

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

然而,如果我在 DataBlock.NFetch 中不使用指针,我就不会遇到这个问题。
但是,如果我这样做,我就失去了直接从映射中更改 NFetch 值的能力,而不需要重新分配一个新的结构体对象给映射中的哈希值,这在计算上相对昂贵。我想在不重新分配整个结构体的情况下更改 NFetch 的值,以避免数据竞争。有什么解决方案吗?

对于 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()
}
英文:

Some starter code goes here,

func (chm *ConcurrentHashMap) NFetchWorker() {
	for {
		key := &lt;-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 looks like this,

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

Struct DataBlock looks like this,

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

Now when I try to run tests with race flag enabled. I get,

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

The line numbers point to this line.

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

However I don't face this issue when I do not use pointer in DataBlock.NFetch.
But if I do that I lose the ability to make changes to NFetch directly from map without reassigning a whole new struct object to that hash in map, which would be relatively computationally expensive. I want to change the value of NFetch without reassigning the whole struct again for one small change while being free from DATA RACE. Any solutions??

Fairly New to Golang, I could be doing something really stupid here, feel free to point it out.

UPDATE: Adding Read and Write Op function

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

	return constants.NIL
}

Write Op

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()
}

答案1

得分: 2

问题出在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
}
英文:

The problem is in the CheckValueValidity where you are accessing NFetch without locking it. Pointers should never be accessed without locking them.

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

	if value.IsUsingNFetch &amp;&amp; *(value.NFetch) &lt;= 0 {
		return false
	}

	return true
}

This code should work

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

    return constants.NIL
}

huangapple
  • 本文由 发表于 2021年9月15日 00:21:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/69181316.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定