gpt4 book ai didi

go - 从按值传递的结构值读取字段时的数据竞争

转载 作者:IT王子 更新时间:2023-10-29 01:26:38 25 4
gpt4 key购买 nike

为什么 golang race detector 报错如下代码:

package main

import (
"fmt"
"sync"
)

type Counter struct {
value int
mtx *sync.Mutex
}

func NewCounter() *Counter {
return &Counter {0, &sync.Mutex{}}
}

func (c *Counter) inc() {
c.mtx.Lock()
c.value++
c.mtx.Unlock()
}

func (c Counter) get() int {
c.mtx.Lock()
res := c.value
c.mtx.Unlock()
return res
}

func main() {
var wg sync.WaitGroup
counter := NewCounter()
max := 100
wg.Add(max)

// consumer
go func() {
for i := 0; i < max ; i++ {
value := counter.get()
fmt.Printf("counter value = %d\n", value)
wg.Done()
}
}()
// producer
go func() {
for i := 0; i < max ; i++ {
counter.inc()
}
}()

wg.Wait()
}

当我使用 -race 运行上面的代码时,我收到以下警告:

==================
WARNING: DATA RACE
Read at 0x00c0420042b0 by goroutine 6:
main.main.func1()
main.go:39 +0x72

Previous write at 0x00c0420042b0 by goroutine 7:
main.(*Counter).inc()
main.go:19 +0x8b
main.main.func2()
main.go:47 +0x50

Goroutine 6 (running) created at:
main.main()
main.go:43 +0x167

Goroutine 7 (running) created at:
main.main()
main.go:49 +0x192
==================

如果我将 func (c Counter) get() int 更改为 func (c *Counter) get() int 那么一切正常。事实证明,get() 函数的接收者类型应该是一个指针。我很困惑为什么会这样。我知道“-copylocks”,但在这种情况下 mtx 是一个指针,而不是值。如果我将“mtx”更改为值并使用 vet -copylocks 运行程序,我会收到此警告:

main.go:23: get passes lock by value: main.Counter contains sync.Mutex`

这是有道理的。

注意:这个问题不是关于如何实现线程安全计数器

link to playground code

最佳答案

竞争是因为 get() 方法的值接收者。为了调用 get() 方法,必须将结构的副本传递给方法表达式。没有语法糖的方法调用如下所示:

value := Counter.get(*counter)

复制结构需要读取 value 字段,这发生在方法可以获取锁之前,这就是为什么在方法调用行而不是方法内部报告竞争的原因。

这就是为什么将接收器更改为指针接收器可以解决问题的原因。此外,由于所有接收器都需要是指针,因此 mtx 可以保留为 sync.Mutex 值,因此不需要对其进行初始化。

关于go - 从按值传递的结构值读取字段时的数据竞争,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42034178/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com