美文网首页
Go——内存模型

Go——内存模型

作者: 懒无趣 | 来源:发表于2020-12-27 22:25 被阅读0次

什么是内存模型

The Go memory model specifies the conditions under which reads of a variable in one goroutine can be guaranteed to observe values produced by writes to the same variable in a different goroutine.

内存模型指导的是不同goroutine对内存中同一变量进行读写操作对同步性。

Happens Before

Within a single goroutine, the happens-before order is the order expressed by the program.

在单一goroutine的情况下,happends-before 的顺序就是程序锁表达的顺序

A read r of a variable v is allowed to observe a write w to v if both of the following hold:
r does not happen before w.
There is no other write w' to v that happens after w but before r.
To guarantee that a read r of a variable v observes a particular write w to v, ensure that w is the only write r is allowed to observe. That is, r is guaranteed to observe w if both of the following hold:
w happens before r.
Any other write to the shared variable v either happens before w or after r.
This pair of conditions is stronger than the first pair; it requires that there are no other writes happening concurrently with w or r.

如果要保证对“变量 v 的读操作 r”能够观察到一个对“变量 v 的写操作 w”,并且 r 只能观察到 w 对变量 v 的写,没有其它对 v 的写操作,也就是说,我们要保证 r 绝对能观察到 w 操作的结果,那么就需要同时满足两个条件:w happens before r;其它对 v 的写操作(w2、w3、w4, …) 要么 happens before w,要么 happens after r,绝对不会和 w、r 同时发生,或者是在它们之间发生。

Within a single goroutine, there is no concurrency, so the two definitions are equivalent: a read r observes the value written by the most recent write w to v. When multiple goroutines access a shared variable v, they must use synchronization events to establish happens-before conditions that ensure reads observe the desired writes.

对于多个goroutine访问共享内存下的变量v,必须使用同步原语(synchronization events)来保证读写顺序。

The initialization of variable v with the zero value for v's type behaves as a write in the memory model.

变量v的零值初始化可以看作是一次写操作

Reads and writes of values larger than a single machine word behave as multiple machine-word-sized operations in an unspecified order.

读写一个大于一个机器字节(machine word,64位系统位64bit,32位则位32bit)的操作可以看作是随机的(即不可保证顺序的正确性)

Synchronization

Init函数

Initialization
Program initialization runs in a single goroutine, but that goroutine may create other goroutines, which run concurrently.
If a package p imports package q, the completion of q's init functions happens before the start of any of p's.
The start of the function main.main happens after all init functions have finished.

  1. 如果包p import 了包q,那p包运行必须在q包init函数执行完之后(q包init函数 happens before p包全部运行)
  2. main.main 函数 必须在 init函数完成之后执行 (init happens before main)

Goroutine creation

The go statement that starts a new goroutine happens before the goroutine's execution begins.

var a string

func f() {
    print(a)
}

func hello() {
    a = "hello, world"
    go f()
}

go语句 happens before go出去的goroutine代码执行,即上述代码能保证a先赋值(a= "hello, world"),然后在读a的值(print(a))。

Goroutine destruction

The exit of a goroutine is not guaranteed to happen before any event in the program.
If the effects of a goroutine must be observed by another goroutine, use a synchronization mechanism such as a lock or channel communication to establish a relative ordering

var a string

func hello() {
    go func() { a = "hello" }()
    print(a)
}

goroutine的退出是不能被保证happen before的,上述的代码是不能保证print(a)可以读到"hello"的值
这个时候就需要用到其他同步原语(synchronization mechanism)来确保你想要的顺序。

Channel communication

buffered channel

A send on a channel happens before the corresponding receive from that channel completes.

var c = make(chan int, 10)
var a string

func f() {
    a = "hello, world"
    c <- 0
}

func main() {
    go f()
    <-c
    print(a)
}

在一个有缓冲区的channel上,同一次序(比如手都是第2个)的发送操作 happen before 接受操作。即当channel没有数据时,<-chan 会阻塞,只有当有数据发送完成时,接受操作才会继续往下执行

The closing of a channel happens before a receive that returns a zero value because the channel is closed

即上述的代码中将 c <- 0替换为close(c)也能达到同样的效果。

unbuffered channel

A receive from an unbuffered channel happens before the send on that channel completes.

var c = make(chan int)
var a string

func f() {
    a = "hello, world"
    <-c
}

func main() {
    go f()
    c <- 0
    print(a)
}

在一个无缓冲区的channel上,接受操作是 happen before 发送操作的,即发送操作会一直阻塞等待有其他goroutine来接受并完成时才会继续执行。

Lock

The sync package implements two lock data types, sync.Mutex and sync.RWMutex.
For any sync.Mutex or sync.RWMutex variable l and n < m, call n of l.Unlock() happens before call m of l.Lock() returns.

var l sync.Mutex
var a string

func f() {
    a = "hello, world"
    l.Unlock()
}

func main() {
    l.Lock()
    go f()
    l.Lock()
    print(a)
}
  1. 第 n 次的 m.Unlock 一定 happens before 第 n+1 m.Lock 方法的返回;
  2. 对于读写锁 RWMutex m,如果它的第 n 个 m.Lock 方法的调用已返回,那么它的第 n 个 m.Unlock 的方法调用一定 happens before 任何一个 m.RLock 方法调用的返回,只要这些 m.RLock 方法调用 happens after 第 n 次 m.Lock 的调用的返回。这就可以保证,只有释放了持有的写锁,那些等待的读请求才能请求到读锁。
  3. 对于读写锁 RWMutex m,如果它的第 n 个 m.RLock 方法的调用已返回,那么它的第 k (k<=n)个成功的 m.RUnlock 方法的返回一定 happens before 任意的 m.RUnlockLock 方法调用,只要这些 m.Lock 方法调用 happens after 第 n 次 m.RLock。

Once

A single call of f() from once.Do(f) happens (returns) before any call of once.Do(f) returns.

var a string
var once sync.Once

func setup() {
    a = "hello, world"
}

func doprint() {
    once.Do(setup)
    print(a)
}

func twoprint() {
    go doprint()
    go doprint()
}

once.Do(func) 是 happen after func函数执行完成的,即doprint函数中必然能保证print(a)这行执行的时候,setup函数已经执行完成。

Reference

Go内存模型

相关文章

  • go 内存模型简要说明

    go 内存模型 大体上来说go的内存是先申请一大片内存,然后将内存分为各个小的span来管理,因为每个go对象有对...

  • Go——内存模型

    什么是内存模型 The Go memory model specifies the conditions unde...

  • go的内存模型

    Go内存模型 介绍 Go的内存模型指定了一个条件,在该条件下,可以保证在一个goroutine中读取便利,以观察通...

  • Go的内存模型

    介绍 如何保证在一个goroutine中看到在另一个goroutine修改的变量的值,这篇文章进行了详细说明。 建...

  • Go 的内存模型

    介绍 Go 的内存模型是可以让多个 goroutine 共享数据的,但指定了条件,在这种条件下,保证一个 goro...

  • Go 内存模型(The Go Memory Model)

    原文链接:https://golang.org/ref/mem,2014.05.31日的版本。自翻,仅供参考。 I...

  • go-map源码简单分析(map遍历为什么时随机的)

    GO 中map的底层是如何实现的 首先Go 语言采用的是哈希查找表,并且使用链表解决哈希冲突。 GO的内存模型 先...

  • 白话Go内存模型&Happen-Before

    来自公#众#号:Gopher指北[https://isites.github.io/] Go内存模型明确指出,一个...

  • golang并发总结

    golang并发模型 go在语言层面提供了内置的并发支持 不要通过共享内存来通信,而应该通过通信来共享内存 并发与...

  • Flink JobManager | TaskManager内存

    Flink内存模型分析 JobManager内存模型 TaskManager内存模型 内存模型分析 Flink使用...

网友评论

      本文标题:Go——内存模型

      本文链接:https://www.haomeiwen.com/subject/xlkanktx.html