The Go Memory Model

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.

Within a single goroutine, 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.

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

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

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.

Defining happens before

  • When a package imports another package, the completion of the imported package’s init functions happens before the start of any of the source package’s
  • A go statement happens before the goroutine begins execution
  • Channels
    • Channel close happens before a receive on the closed channel completes
    • Buffered channel: Completion of a channel send happens before the corresponding receive (assuming there is one) completes
    • Unbuffered channel: Completion of a channel receieve happens before the corresponding send
    • All channels: The $k$th receive on a channel with capacity $C$ happens before the $k+C$th send from that channel completes
  • 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.
  • A single call of f() from once.Do(f) happens before (returns) any call of once.Do(f) returns.

Incorrect Synchronization

In this program:

var a, b int

func f() {
	a = 1
	b = 2
}

func g() {
	print(b)
	print(a)
}

func main() {
	go f()
	g()
}

it can happen that g prints 2 and then 0. More formally:

A read $r$ may observe the value written by a write $w$ that happens concurrently with $r$. Even if this occurs, it does not imply that reads happening after $r$ will observe writes that happened before $w$.

Edit