在戴夫切尼博客中,下面的代码显然会导致一种只通过将func (RPC) version() int更改为func (*RPC) version() int就可以解决的争用情况:
package main
import (
"fmt"
"time"
)
type RPC struct {
result int
done chan struct{}
}
func (rpc *RPC) compute() {
time.Sleep(time.Second) // strenuous computation intensifies
rpc.result = 42
close(rpc.done)
}
func (RPC) version() int {
return 1 // never going to need to change this
}
func main() {
rpc := &RPC{done: make(chan struct{})}
go rpc.compute() // kick off computation in the background
version := rpc.version() // grab some other information while we're waiting
<-rpc.done // wait for computation to finish
result := rpc.result
fmt.Printf("RPC computation complete, result: %d, version: %d\n", result, version)
}在看了几次代码之后,我很难相信这段代码有一个竞赛用例。然而,当使用--race运行时,它声称在rpc.result=42上有一个写,在version := rpc.version()上有一个先前的读。我理解写,因为rpc.result的值会发生变化,但是读又会怎样呢?在version()方法中,读取在哪里发生?它不触及rpc的任何值,只返回1。
我想了解以下几点:
1)为什么这个特定的行被认为是对rpc结构的读取?
2)为什么要将RPC改为*RPC来解决种族问题?
发布于 2016-08-19 15:05:59
当您有一个具有如下值接收者的方法时:
func (RPC) version() int {
return 1 // never going to need to change this
}你把这个方法叫做:
version := rpc.version() // grab some other information while we're waiting必须从值rpc中复制一个副本,该值将传递给方法(用作接收方值)。
因此,当一个goroutine go rpc.compute()正在运行并正在修改rpc结构值(rpc.result = 42)时,主goroutine正在复制整个rpc结构值。那里!这是场比赛。
将接收方类型修改为指针时:
func (*RPC) version() int {
return 1 // never going to need to change this
}你把这个方法叫做:
version := rpc.version() // grab some other information while we're waiting这是一个缩写
version := (&rpc).version()这将rpc值的地址传递给RPC.version(),它只使用指针作为接收方,因此不会复制rpc结构值。而且,由于在RPC.version()中没有使用/读取结构中的任何内容,所以不存在竞争。
注:
请注意,如果RPC.version()将读取RPC.result字段,它也将是一个竞赛,因为一个goroutine修改它,而主goroutine将读取它:
func (rpc *RPC) version() int {
return rpc.result // RACE!
}注2:
还请注意,如果RPC.version()将读取未在RPC.compute()中修改的另一个RPC字段,则这将不是一个竞赛,例如:
type RPC struct {
result int
done chan struct{}
dummy int
}
func (rpc *RPC) version() int {
return rpc.dummy // Not a race
}https://stackoverflow.com/questions/39042074
复制相似问题