duafagm1066 2018-04-05 19:21
浏览 49
已采纳

了解Go的内存模型

A seemingly clever trick to avoid locking in concurrent C code goes like this: I have a global variable ptr which points to a mystruct and I want to update that structure. So I'll allocate a new mystruct, fill the data in and only then I'll make the change visible to the world by pointing ptr to the new mystruct object.

This is incorrect as it depends on the ordering of writes and there's no guarantee that the write to ptr will become visible to other threads after all stores to the new mystruct have taken place. Therefore, the new mystruct object can be returned partially initialized.

My question is: can this happen in Go, too? I think it can, but I have to say I found The Go Memory Model a little incomprehensible.

I wrote a bit of Go code to test it out, but on my machine, the bad behavirour does not manifest itself:

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 {}
}

This of course proves nothing.

Can you please provide a very brief comparison of memory guarantees provided by Go's goroutines with (almost no guarantees) provided by a POSIX thread in C?

  • 写回答

1条回答 默认 最新

  • dsk61780 2018-04-05 19:24
    关注

    The Go Memory Model

    Version of May 31, 2014

    Advice

    If you must read the rest of this document to understand the behavior of your program, you are being too clever.

    Don't be clever.


    Introducing the Go Race Detector


    I [David] wrote a bit of Go code to test it out.

    Your Go program has data races. The results are undefined.

    $ 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>>
    

    Your Go program: 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 {}
    }
    

    Playground: https://play.golang.org/p/XKywmzrRRRw

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 MPI读取tif文件无法正常给各进程分配路径
  • ¥15 如何用MATLAB实现以下三个公式(有相互嵌套)
  • ¥30 关于#算法#的问题:运用EViews第九版本进行一系列计量经济学的时间数列数据回归分析预测问题 求各位帮我解答一下
  • ¥15 setInterval 页面闪烁,怎么解决
  • ¥15 如何让企业微信机器人实现消息汇总整合
  • ¥50 关于#ui#的问题:做yolov8的ui界面出现的问题
  • ¥15 如何用Python爬取各高校教师公开的教育和工作经历
  • ¥15 TLE9879QXA40 电机驱动
  • ¥20 对于工程问题的非线性数学模型进行线性化
  • ¥15 Mirare PLUS 进行密钥认证?(详解)