本文介绍了为什么不读/写其内容的结构方法仍然导致竞争情况?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

来自戴夫·切尼博客,以下代码显然会导致种族问题,仅通过将 func(RPC)version()int 更改为 func(* RPC)version()int 即可解决种族问题:

From the Dave Cheney Blog, the following code apparently causes a race case that can be resolved merely by changing func (RPC) version() int to 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()处有一个先前的读取.我了解写操作,因为goroutine更改了 rpc.result 的值,但是读操作如何?读取在 version()方法中的何处进行?它不涉及任何rpc值,仅返回1.

After looking over the code a few times, I was having a hard time believing that the code had a race case. However, when running with --race, it claims that there was a write at rpc.result=42 and a previous read at version := rpc.version(). I understand the write, since the goroutine changes the value of rpc.result, but what about the read? Where in the version() method does the read occur? It does not touch any of the values of rpc, just returning 1.

我想了解以下内容:

1)为什么将特定行视为rpc结构上的读?

1) Why is that particular line considered a read on the rpc struct?

2)为什么将 RPC 更改为 * RPC 可以解决种族问题?

2) Why would changing RPC to *RPC resolve the race case?

推荐答案

当您的方法具有这样的值接收者时:

When you have a method with value receiver like this:

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 复制一个副本,该副本将被传递给方法(用作接收者的值).

A copy has to be made from the value rpc, which will be passed to the method (used as the receiver value).

因此,当一个goroutine go rpc.compute()正在运行并且正在修改 rpc 结构值( rpc.result = 42 )时,主要的goroutine正在复制整个 rpc 结构值.那里!这是一场比赛.

So while one goroutine go rpc.compute() is running and is modifying the rpc struct value (rpc.result = 42), the main goroutine is making a copy of the whole rpc struct value. There! It's a race.

当您将接收器类型修改为指针时:

When you modify the receiver type to pointer:

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()中的结构,因此没有任何竞争.

This passes the address of the rpc value to RPC.version(), it uses only the pointer as the receiver, so no copy is made of the rpc struct value. And since nothing from the struct is used / read in RPC.version(), there is no race.

注意:

请注意,如果 RPC.version()会读取 RPC.result 字段,那也将是一场竞赛,因为一个goroutine会对其进行修改,而主goroutine会对其进行修改阅读:

Note that if RPC.version() would read the RPC.result field, it would also be a race, as one goroutine modifies it while the main goroutine would read it:

func (rpc *RPC) version() int {
    return rpc.result // RACE!
}

注释2:

还要注意,如果 RPC.version()将读取 RPC 的另一个字段,而该字段在 RPC.compute()中未修改,那不会是一场比赛,例如:

Also note that if RPC.version() would read another field of RPC which is not modified in RPC.compute(), that would not be a race, e.g.:

type RPC struct {
    result int
    done   chan struct{}
    dummy  int
}

func (rpc *RPC) version() int {
    return rpc.dummy // Not a race
}

这篇关于为什么不读/写其内容的结构方法仍然导致竞争情况?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-23 17:13