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.
- https://pkg.go.dev/sync
- https://pkg.go.dev/sync/atomic
- The happens before ordering is a partial order that is analogous to the lexical order of the code
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 forv
’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
orsync.RWMutex
variablel
and $n < m$, call $n$ ofl.Unlock()
happens before call $m$ ofl.Lock()
returns. - A single call of
f()
fromonce.Do(f)
happens before (returns) any call ofonce.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$.