This is a creation in Article, where the information may have evolved or changed.
Http://golang.org/ref/mem
The Go Memory Model
Version of March 6, 2012
-
Introduction
-
Happens before
-
Synchronization
-
initialization
-
Goroutine Creation
-
Goroutine Destruction
-
Channel Communication
-
Locks
-
Once
-
Incorrect synchronization
|
|
Introduction
The Go memory model specifies the conditions under whichreads of a variable in one goroutine can be guaranteed Toobserve V Alues produced by writes to the same variable in a different goroutine.
Happens before
Within a single goroutine, reads and writes must behaveas if they executed in the order specified by the program. That's, compilers and processors may reorder the reads and writesexecuted within a single goroutine if the reorder Ingdoes the behavior within that goroutineas defined by the language specification. Because of this reordering, the execution order Observedby one goroutine could differ from the order Perceivedby another. For example, if one goroutineexecutes a = 1; b = 2;
, another might observethe updated value of b
before the updated value Of .
To specify the requirements of reads and writes, we definehappensbefore, a partial order on the EXECUTIONOF memo RY operations in a Go program. If EventE1 Happensbefore event E2, then we say this E2 happens afterE1. Also, if E1 does not happen before E2 and does not happenafter E2, then we say that E1 andE2 happen concurrently.
Within a single goroutine, the Happens-before order was theorder expressed by the program.
A read R of a variable v
is allowed to observe a writeW to v
if both of the foll Owing hold:
- R does not happen before W.
- There is no other write W ' to that
v
happens afterW but before R.
To guarantee the A read R of a variable v
observes Aparticular writeW v
to, ensure that W is the OnlywriteR was allowed to observe. That's, R isguaranteed to observe W if both of the following hold:
- W happens before R.
- Any and write to the GKFX variable
v
either happens before W or after R.
This pair of conditions was stronger than the first pair;it requires that there be no other writes happeningconcurrently W ITHW or R.
Within a single goroutine,there was no concurrency, so the the other definitions is equivalent:a readR observes the Val UE written by the most recent write W to v
. When multiple goroutines access a shared variable v
, they must use synchronization events to Establishhappens-before Co Nditions that ensure reads observe Thedesired writes.
The initialization of variable with the v
zero valuefor v
' s type behaves as a write in the memory model.
Reads and writes of values larger than a single machine wordbehave as multiple machine-word-sized operations in ANUNSPECIF IED order.
Synchronization
Initialization
Program initialization runs with a single goroutine,but that Goroutine could create other Goroutines,which run concurrently.
If A Package p
imports package q
, the completion of q
' s init
functions happens before the start p
of any of ' s.
The start of the function main.main
happens afterall init
functions has finished.
Goroutine creation
The go
statement that starts a new goroutinehappens before the Goroutine ' s execution begins.
For example, the This program:
var a stringfunc f () {print (a)}func hello () {a = "Hello, world" Go f ()}
Calling would print at some point in the future hello
"hello, world"
(perhaps after have hello
returned).
Goroutine Destruction
The exit of a goroutine is not guaranteed to happen Beforeany event in the program. For example, the This program:
var a stringfunc hello () {go func () {a = "Hello"} () print (a)}
a
the assignment to isn't followed Byany synchronization event, so it's not guaranteed to beobserved by any other go Routine. In fact, an aggressive compiler might delete the entire go
statement.
If the effects of a goroutine must be observed by another goroutine,use a synchronization mechanism such as a lock or Chan Nelcommunication to establish a relative ordering.
Channel Communication
Channel communication is the main method of Synchronizationbetween Goroutines. Each of the particular channelis matched to a corresponding receive is from the channel,usually in a different goroutine.
A Send on a channel happens before the correspondingreceive from that channel completes.
This program:
var c = make (chan int, ten) var a stringfunc f () {a = "Hello, world" C <-0}func main () {go f () <-cprint (a)}
is guaranteed to print "hello, world"
. The write to a
happens before the send c
in, which happens beforethe corresponding receive on c
completes, which Happens Beforethe print
.
The closing of a channel happens before a receive that returns a zero valuebecause the channel is closed.
In the previous example, replacing and yields a program with the c <- 0
close(c)
same guaranteed behavior.
A receive from the Unbuffered channel happens beforethe Send on the that channel completes.
This program (as above, but with the send and receive statements swapped andusing an unbuffered channel):
var c = make (chan int) var a stringfunc f () {a = ' Hello, world ' <-c}
Func main () {go f () C <-0print (a)}
is also guaranteed to print "hello, world"
. a
the write to happens before the receive c
in, which happens beforethe corresponding send on c
completes, which Happensbefore the print
.
If the channel were buffered (e.g.,) then the program would isn't being c = make(chan int, 1)
guaranteed to print "hello, world"
. (It might print the empty string,crash, or do something else.)
Locks
sync
The package implements the data types, and sync.Mutex
sync.RWMutex
.
For any sync.Mutex
or sync.RWMutex
variable l
and n < m, call n of l.Unlock()
happens before callm< /c12> of l.Lock()
returns.
This program:
var l sync. Mutexvar a stringfunc f () {a = "Hello, World" l.unlock ()}func main () {l.lock () go f () L.lock () print (a)}
is guaranteed to print "hello, world"
. The first call to (in) Happensbefore the second call to l.Unlock()
f
(in l.Lock()
main
) Returns,which happens before print
the .
l.RLock
sync.RWMutex
l
for variable, there are an n such that the l.RLock
happens (returns) after Call n to and the l.Unlock
matching l.RUnlock
happensbefore call n+1 to l.Lock
.
Once
The package sync
provides a safe mechanism forinitialization in the presence of multiple Goroutinesthrough the use of th e Once
type. Multiple threads can execute for once.Do(f)
a particular f
, but only one would run f()
, and the other calls Blockuntil has returned.
A single Call of f()
from once.Do(f)
happens (returns) before any call of once.Do(f)
returns.
In the program:
var a stringvar once sync. Oncefunc Setup () {a = "Hello, World"}func doprint () {once. Do (Setup) print (a)}func twoprint () {go doprint () Go Doprint ()}
Calling twoprint
causes to be "hello, world"
printed twice. The first call to twoprint
runs setup
once.
Incorrect synchronization
Note that a read R is observe the value written by a writeW, happens concurrently with R. Even if this occurs, it does not imply that reads happening afterRwould observe writes that happened before w.
In the program:
var A, B intfunc f () {a = 1b = 2}func g () {print (b) print (a)}func main () {go f () g ()}
It can happen that g
prints and then 2
0
.
This fact invalidates a few common idioms.
Double-checked locking is a attempt to avoid the overhead of synchronization. For example, the program twoprint
might beincorrectly written as:
var a stringvar done Boolfunc setup () {a = ' Hello, world ' done = True}func Doprint () {if!done {once. Do (Setup)}print (a)}func twoprint () {go doprint () Go Doprint ()}
But there was no guarantee that, doprint
in, observing the write to done
implies observing the write to a
. Thisversion can (incorrectly) print an empty stringinstead of "hello, world"
.
Another incorrect idiom is busy waiting for a value, as in:
var a stringvar done Boolfunc setup () {a = ' Hello, world ' done = True}func main () {go setup () for!done {}print (a)}
As before, there is no guarantee, in main
, observing the write done
to implies observing a
the write to, so this P Rogram couldprint an empty string too. Worse, there is no guarantee then the write to would done
Everbe observed main
by, since there was no synchronizationevent s between the threads. The loop in was main
Notguaranteed to finish.
There is subtler variants on this theme, such as this program.
Type T struct {msg string}var g *tfunc Setup () {t: = new (t) t.msg = "Hello, world" G = T}func main () {go setup () for g = = Nil {}print (g.msg)}
Even if main
observes and g != nil
exits its loop,there are no guarantee that it'll observe the initializedvalue for g.msg
.
In all these examples, the solution is the Same:use explicit synchronization.