了解Go的内存模型

时间:2018-04-05 19:21:28

标签: go memory-management shared-memory

避免锁定并发C代码的一个看似聪明的技巧是这样的:我有一个全局变量ptr指向mystruct,我想更新该结构。因此,我将分配一个新的mystruct,填充中的数据,然后才会通过将ptr指向该世界,我可以将更改显示给全世界新的mystruct对象。

这是不正确的,因为它取决于写入的顺序,并且不能保证在对新ptr的所有存储都采取后,对mystruct的写入将对其他线程可见地点。因此,可以返回部分初始化的新mystruct对象。

我的问题是:这也可以在Go中发生吗?我认为可以,但我不得不说我发现The Go Memory Model有点难以理解。

我写了一些Go代码来测试它,但在我的机器上,坏的行为并没有表现出来:

package main

import (
    "fmt"
    "time"
)

type mystruct struct {
    a int
    b int
}

var (
    ptr *mystruct
    counter int
)

func writer() {
    for {
        counter += 1
        s := mystruct{a: counter, b: counter}
        ptr = &s
    }
}

func reader() {
    time.Sleep(time.Millisecond)
    for {
        if ptr.a != ptr.b {
            fmt.Println("Oh no, I'm so buggy!")
        }
    }
}

func main() {
    go writer()
    go reader()
    select {}
}

这当然没有任何证据。

请您简单比较一下Go的goroutines提供的内存保证与C中POSIX线程提供的(几乎没有保证)?

1 个答案:

答案 0 :(得分:2)

  

The Go Memory Model

     

2014年5月31日的版本

     

建议

     

如果您必须阅读本文档的其余部分以了解该行为   你的计划,你太聪明了。

     

不要聪明。

  

Introducing the Go Race Detector

  

我[大卫]写了一些Go代码来测试它。

你的Go程序有数据竞赛。结果未定义。

$ go run -race david.go
==================
WARNING: DATA RACE
Read at 0x000000596cc0 by goroutine 7:
  main.reader()
      /home/peter/gopath/src/david.go:29 +0x4b

Previous write at 0x000000596cc0 by goroutine 6:
  main.writer()
      /home/peter/gopath/src/david.go:22 +0xf8

Goroutine 7 (running) created at:
  main.main()
      /home/peter/gopath/src/david.go:37 +0x5a

Goroutine 6 (running) created at:
  main.main()
      /home/peter/gopath/src/david.go:36 +0x42
==================
==================
WARNING: DATA RACE
Read at 0x00c0000cc270 by goroutine 7:
  main.reader()
      /home/peter/gopath/src/david.go:29 +0x5b

Previous write at 0x00c0000cc270 by goroutine 6:
  main.writer()
      /home/peter/gopath/src/david.go:21 +0xd2

Goroutine 7 (running) created at:
  main.main()
      /home/peter/gopath/src/david.go:37 +0x5a

Goroutine 6 (running) created at:
  main.main()
      /home/peter/gopath/src/david.go:36 +0x42
==================
==================
WARNING: DATA RACE
Read at 0x00c0000cda38 by goroutine 7:
  main.reader()
      /home/peter/gopath/src/david.go:29 +0x7f

Previous write at 0x00c0000cda38 by goroutine 6:
  main.writer()
      /home/peter/gopath/src/david.go:21 +0xd2

Goroutine 7 (running) created at:
  main.main()
      /home/peter/gopath/src/david.go:37 +0x5a

Goroutine 6 (running) created at:
  main.main()
      /home/peter/gopath/src/david.go:36 +0x42
==================
<<SNIP>>

你的Go计划:david.go

package main

import (
    "fmt"
    "time"
)

type mystruct struct {
    a int
    b int
}

var (
    ptr     *mystruct
    counter int
)

func writer() {
    for {
        counter += 1
        s := mystruct{a: counter, b: counter}
        ptr = &s
    }
}

func reader() {
    time.Sleep(time.Millisecond)
    for {
        if ptr.a != ptr.b {
            fmt.Println("Oh no, I'm so buggy!")
        }
    }
}

func main() {
    go writer()
    go reader()
    select {}
}

游乐场:https://play.golang.org/p/XKywmzrRRRw