pointers - 如何在 golang 中创建真正的比赛条件

标签 pointers go concurrency race-condition goroutine

问题描述

最近我学会了选项-race检查 go 中是否存在竞争条件。完整的命令是 go run -race xxx.go它真的帮了我很多。但是与下面的代码一样,我认为检查结果是错误的,并尝试了很多方法( 我尝试得到一个 panic 下面)得到一个 真实 panic 但失败了。所以我想知道代码是否正确以及比赛检查是否错误,或者你可以修改我的代码以便我可以真正的 panic 。非常感谢。

编码

package main
import "fmt"

type myType struct {
    A int
}

func main(){
    c:=make(chan bool)
    x := new(myType)
    go func(){
        x = new(myType) // write to x
        c <- true
    }()
    _ = *x // read from x
    <-c
    fmt.Println("end")
}

比赛检查结果
go run -race test.go
==================
WARNING: DATA RACE
Write at 0x00c00009c010 by goroutine 6:
  main.main.func1()
      /Users/yaodongen/test.go:12 +0x56

Previous read at 0x00c00009c010 by main goroutine:
  main.main()
      /Users/yaodongen/test.go:15 +0xe2

Goroutine 6 (running) created at:
  main.main()
      /Users/yaodongen/test.go:11 +0xd4
==================
end
Found 1 data race(s)
exit status 66

我的观点

我试图找到比赛条件报告的原因。在 post (中文),它提到操作a = in64(0)不是原子的。例如在一台 32 位机器中,像 int64 这样的数据可能是 64 位长度,CPU 可以复制一半的数据并被其他数据中断。在下面的代码( 证明 golang 副本不是原子的 )中,我编写了一个代码来证明它的真实性。但就我而言,代码 x = new(myType)就是复制一个指针值,我认为可以在一个CPU副本中完成。换句话说,操作是原子的,永远不会达到竞争条件。

证明 golang 副本不是原子的
package main
import "fmt"
import "time"

func main(){
    var x = [...]int{1,1,1,1,1,1}
    c := make(chan int, 100)
    go func(){
        for i:=0;;i++{
            if i&1 == 0 {
                x = [...]int{2,2,2,2,2,2} // write to x
            }else{
                x = [...]int{1,1,1,1,1,1} // write to x
            }
            c<-0 // let other goroutine see the change of x
            <-c
        }
    }()
    go func(){
        for {
            d := x                    // read from x
            if d[0] != d[5] {
                fmt.Println(d)
                panic("error")        // proved the copy operation is not atomic
            }
            c<-0
            <-c
        }
    }()
    time.Sleep(time.Millisecond * 10000)
    fmt.Println("end")
}

我试图引起 panic

但它失败了,如果存在竞争条件(错误的内存地址),代码会 panic 。
package main
import "fmt"
import "time"

type myType struct {
    A int
}

func main(){
    x := new(myType)
    c := make(chan int, 100)
    go func(){
        for {
            x = new(myType) // write to x
            c<-0
            <-c
        }
    }()
    for i:=0; i<4; i++{
        go func(){
            for {
                _ = *x // if exists a race condition, `*x` will visit a wrong memory address, and will panic
                c<-0
                <-c
            }
        }()
    }
    time.Sleep(time.Second * 10)
    fmt.Println("end")
}

最佳答案

Go 的种族检测永远不会给出误报。如果它告诉你有一场比赛,那么就有一场比赛。它可能无法识别所有种族(它们必须碰巧是可检测的),但它发现的总是积极的(种族检测器中的错误不算在内)。

您的示例中的竞争条件清晰而简单。你有 2 个 goroutine,一个读取一个变量,另一个不同步地写入它。这是竞争条件的秘诀。

竞争条件使您的应用程序无法预测。竞争条件的行为是未定义的。任何有经验的行为都属于未定义行为,包括缺乏 panic 。不要引诱魔鬼,如果存在竞争条件,请使用适当的同步。故事结局。

Is it safe to read a function pointer concurrently without a lock?

关于pointers - 如何在 golang 中创建真正的比赛条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59089844/

相关文章:

c - C中使用指针从字符串中提取子字符串

c - 如何正确使用多维数组中的指针?

go - 如何避免tempCodeRunnerFile.go出现在项目中

C++ 指向数组的指针

string - 在Go中解析csv文件,第一行第一列中有多余的字节

go - 最重要的测试用例

没有互斥量的条件等待

java - @GuardedBy , @ThreadSafe ,@NotThreadSafe

c++ - 现代x86硬件能否不将单个字节存储到内存中?

c - 使用指针手动复制字符串时出错