mirror of
https://github.com/golang/go
synced 2024-11-22 01:04:40 -07:00
draft of memory model.
R=tgs DELTA=545 (545 added, 0 deleted, 0 changed) OCL=25212 CL=25268
This commit is contained in:
parent
ff70f09d27
commit
82c38cf8dd
457
doc/mem.html
Normal file
457
doc/mem.html
Normal file
@ -0,0 +1,457 @@
|
||||
<h1>The Go memory model</h1>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<h2>Happens Before</h2>
|
||||
|
||||
<p>
|
||||
Within a single goroutine, reads and writes must behave
|
||||
as if they executed in the order specified by the program.
|
||||
That is, compilers and processors may reorder the reads and writes
|
||||
executed within a single goroutine only when the reordering
|
||||
does not change the execution behavior within that goroutine.
|
||||
Because of this reordering, the execution order observed
|
||||
by one may differ from the order perceived
|
||||
by another. For example, if one goroutine
|
||||
executes <code>a = 1; b = 2;</code>, a second goroutine might observe
|
||||
the updated value of <code>b</code> before the updated value of <code>a</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To specify the requirements on reads and writes, we define
|
||||
<i>happens before</i>, a partial order on the execution
|
||||
of memory operations in a Go program. If event <span class="event">e<sub>1</sub></span> happens
|
||||
before event <span class="event">e<sub>2</sub></span>, then we say that <span class="event">e<sub>2</sub></span> happens after <span class="event">e<sub>1</sub></span>.
|
||||
Also, if <span class="event">e<sub>1</sub></span> does not happen before <span class="event">e<sub>2</sub></span> and does not happen
|
||||
after <span class="event">e<sub>2</sub></span>, then we say that <span class="event">e<sub>1</sub></span> and <span class="event">e<sub>2</sub></span> happen concurrently.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Within a single goroutine, the happens before order is the
|
||||
order specified by the program.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A read <span class="event">r</span> of a variable <code>v</code> is <i>allowed</i> to observe a write <span class="event">w</span> to <code>v</code>
|
||||
if both of the following hold:
|
||||
</p>
|
||||
|
||||
<ol>
|
||||
<li><span class="event">w</span> happens before <span class="event">r</span>.</li>
|
||||
<li>There is no other write <span class="event">w'</span> to <code>v</code> that happens
|
||||
after <span class="event">w</span> but before <span class="event">r</span>.</li>
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
To guarantee that a read <span class="event">r</span> of a variable <code>v</code> observes a
|
||||
particular write <span class="event">w</span> to <code>v</code>, ensure that <span class="event">w</span> is the only
|
||||
write <span class="event">r</span> is allowed to observe.
|
||||
That is, <span class="event">r</span> is <i>guaranteed</i> to observe <span class="event">w</span> if both of the following hold:
|
||||
</p>
|
||||
|
||||
<ol>
|
||||
<li><span class="event">w</span> happens before <span class="event">r</span>.</li>
|
||||
<li>Any other write to the shared variable <code>v</code>
|
||||
either happens before <span class="event">w</span> or after <span class="event">r</span>.</li>
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
This pair of conditions is stronger than the first pair;
|
||||
it requires that there are no other writes happening
|
||||
concurrently with <span class="event">w</span> or <span class="event">r</span>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Within a single goroutine,
|
||||
there is no concurrency, so the two definitions are equivalent:
|
||||
a read <span class="event">r</span> observes the value written by the most recent write <span class="event">w</span> to <code>v</code>.
|
||||
When multiple goroutines access a shared variable <code>v</code>,
|
||||
they must use synchronization events to establish
|
||||
happens-before conditions that ensure reads observe the
|
||||
desired writes.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The initialization of variable <code>v</code> with the zero value
|
||||
for <code>v</code>'s type behaves as a write in the memory model.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Reads and writes of values larger than a single machine word
|
||||
behave as multiple machine-word-sized operations in an
|
||||
unspecified order.
|
||||
</p>
|
||||
|
||||
<h2>Synchronization</h2>
|
||||
|
||||
<h3>Initialization</h3>
|
||||
|
||||
<p>
|
||||
Program initialization runs in a single goroutine, and
|
||||
new goroutines created during initialization do not
|
||||
start running until initialization ends.
|
||||
</p>
|
||||
|
||||
<p class="rule">
|
||||
If a package <code>p</code> imports package <code>q</code>, the completion of
|
||||
<code>q</code>'s <code>init</code> functions happens before the start of any of <code>p</code>'s.
|
||||
</p>
|
||||
|
||||
<p class="rule">
|
||||
The start of the function <code>main.main</code> happens after
|
||||
all <code>init</code> functions have finished.
|
||||
</p>
|
||||
|
||||
<p class="rule">
|
||||
The execution of any goroutines created during <code>init</code>
|
||||
functions happens after all <code>init</code> functions have finished.
|
||||
</p>
|
||||
|
||||
<h3>Goroutine creation</h3>
|
||||
|
||||
<p class="rule">
|
||||
The <code>go</code> statement that starts a new goroutine
|
||||
happens before the goroutine's execution begins.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example, in this program:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var a string;
|
||||
|
||||
func f() {
|
||||
print(a);
|
||||
}
|
||||
|
||||
func hello() {
|
||||
a = "hello, world";
|
||||
go f();
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
calling <code>hello</code> will print <code>"hello, world"</code>
|
||||
at some point in the future (perhaps after <code>hello</code> has returned).
|
||||
</p>
|
||||
|
||||
<h3>Channel communication</h3>
|
||||
|
||||
<p>
|
||||
Channel communication is the main method of synchronization
|
||||
between goroutines. Each send on a particular channel
|
||||
is matched to a corresponding receive from that channel,
|
||||
usually in a different goroutine.
|
||||
</p>
|
||||
|
||||
<p class="rule">
|
||||
A send on a channel happens before the corresponding
|
||||
receive from that channel completes.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example, this program:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var c = make(chan int, 10);
|
||||
var a string;
|
||||
|
||||
func f() {
|
||||
a = "hello, world";
|
||||
c <- 0;
|
||||
}
|
||||
|
||||
func main() {
|
||||
go f();
|
||||
<-c;
|
||||
print(a);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
is guaranteed to print <code>"hello, world"</code>. The write to <code>a</code>
|
||||
happens before the send on <code>c</code>, which happens before
|
||||
the corresponding receive on <code>c</code> completes, which happens before
|
||||
the <code>print</code>.
|
||||
</p>
|
||||
|
||||
<p class="rule">
|
||||
A receive from an unbuffered channel happens before
|
||||
the send on that channel completes.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example, this program:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var c = make(chan int);
|
||||
var a string;
|
||||
|
||||
func f() {
|
||||
a = "hello, world";
|
||||
<-c;
|
||||
}
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
func main() {
|
||||
go f();
|
||||
c <- 0;
|
||||
print(a);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
is also guaranteed to print "hello, world". The write to <code>a</code>
|
||||
happens before the receive on <code>c</code>, which happens before
|
||||
the corresponding send on <code>c</code> completes, which happens
|
||||
before the <code>print</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If the channel were buffered (e.g., <code>c = make(chan int, 1)</code>)
|
||||
then the program would not be guaranteed to print
|
||||
<code>"hello, world"</code>. (It might print the empty string;
|
||||
it cannot print <code>"hello, sailor"</code>, nor can it crash.)
|
||||
</p>
|
||||
|
||||
<h3>Locks</h3>
|
||||
|
||||
<p>
|
||||
The <code>sync</code> package implements two lock data types,
|
||||
<code>sync.Mutex</code> and <code>sync.RWMutex</code>.
|
||||
</p>
|
||||
|
||||
<p class="rule">
|
||||
For any <code>sync.Mutex</code> variable <code>l</code> and <i>n</i> < <i>m</i>,
|
||||
the <i>n</i>'th call to <code>l.Unlock()</code> happens before the <i>m</i>'th call to <code>l.Lock()</code> returns.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example, this program:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var l sync.Mutex;
|
||||
var a string;
|
||||
|
||||
func f() {
|
||||
a = "hello, world";
|
||||
l.Unlock();
|
||||
}
|
||||
|
||||
func main() {
|
||||
l.Lock();
|
||||
go f();
|
||||
l.Lock();
|
||||
print(a);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
is guaranteed to print <code>"hello, world"</code>.
|
||||
The first call to <code>l.Unlock()</code> (in <code>f</code>) happens
|
||||
before the second call to <code>l.Lock()</code> (in <code>main</code>) returns,
|
||||
which happens before the <code>print</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
TODO(rsc): <code>sync.RWMutex</code>.
|
||||
</p>
|
||||
|
||||
<h3>Once</h3>
|
||||
|
||||
<p>
|
||||
The <code>once</code> package provides a safe mechanism for
|
||||
initialization in the presence of multiple goroutines.
|
||||
Multiple threads can execute <code>once.Do(f)</code> for a particular <code>f</code>,
|
||||
but only one will run <code>f()</code>, and the other calls block
|
||||
until <code>f()</code> has returned.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A single call to <code>f()</code> happens before <code>once.Do(f)</code> returns.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example, in this program:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var a string;
|
||||
|
||||
func setup() {
|
||||
a = "hello, world";
|
||||
}
|
||||
|
||||
func doprint() {
|
||||
once.Do(setup);
|
||||
print(a);
|
||||
}
|
||||
|
||||
func twoprint() {
|
||||
go doprint();
|
||||
go doprint();
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
calling <code>twoprint</code> causes <code>"hello, world"</code> to be printed twice.
|
||||
The first call to <code>twoprint</code> runs <code>setup</code> once.
|
||||
</p>
|
||||
|
||||
<h2>Incorrect synchronization</h2>
|
||||
|
||||
<p>
|
||||
Note that a read <span class="event">r</span> may observe the value written by a write <span class="event">w</span>
|
||||
that happens concurrently with <span class="event">r</span>.
|
||||
Even if this occurs, it does not imply that reads happening after <span class="event">r</span>
|
||||
will observe writes that happened before <span class="event">w</span>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example, in this program:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var a, b int;
|
||||
|
||||
func f() {
|
||||
a = 1;
|
||||
b = 2;
|
||||
}
|
||||
|
||||
func g() {
|
||||
print(b);
|
||||
print(a);
|
||||
}
|
||||
|
||||
func main() {
|
||||
go f();
|
||||
g();
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
it can happen that <code>g</code> prints <code>2</code> and then <code>0</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This fact invalidates a few obvious idioms.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Double-checked locking is an attempt to avoid the overhead of synchronization.
|
||||
For example, the <code>twoprint</code> program above, might be
|
||||
incorrectly written as:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var a string;
|
||||
var done bool;
|
||||
|
||||
func setup() {
|
||||
a = "hello, world";
|
||||
done = true;
|
||||
}
|
||||
|
||||
func doprint() {
|
||||
if !done {
|
||||
once.Do(setup);
|
||||
}
|
||||
print(a);
|
||||
}
|
||||
|
||||
func twoprint() {
|
||||
go doprint();
|
||||
go doprint();
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
but there is no guarantee that, in <code>doprint</code>, observing the write to <code>done</code>
|
||||
implies observing the write to <code>a</code>. This
|
||||
version can (incorrectly) print an empty string
|
||||
instead of <code>"hello, world"</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Another incorrect idiom is busy waiting for a value, as in:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var a string;
|
||||
var done bool;
|
||||
|
||||
func setup() {
|
||||
a = "hello, world";
|
||||
done = true;
|
||||
}
|
||||
|
||||
func main() {
|
||||
go setup();
|
||||
for !done {
|
||||
}
|
||||
print(a);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
As before, there is no guarantee that, in <code>main</code>,
|
||||
observing of the write to <code>done</code>
|
||||
implies observing the write to <code>a</code>, so this program could
|
||||
print an empty string too.
|
||||
Worse, there is no guarantee that the write to <code>done</code> will ever
|
||||
be observed by <code>main</code>, since there are no synchronization
|
||||
events between the two threads. The loop in <code>main</code> is not
|
||||
guaranteed to finish.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
There are subtler variants on this theme. For example, in this program:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
type T struct {
|
||||
msg string;
|
||||
}
|
||||
|
||||
var g *T;
|
||||
|
||||
func setup() {
|
||||
t := new(T);
|
||||
t.msg = "hello, world";
|
||||
g = t;
|
||||
}
|
||||
|
||||
func main() {
|
||||
go setup();
|
||||
for g == nil {
|
||||
}
|
||||
print(g.msg);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Even if <code>main</code> observes <code>g != nil</code> and exits its loop,
|
||||
there is no guarantee that it will observe the initialized
|
||||
value for <code>g.msg</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In all these examples, the solution is the same:
|
||||
use explicit synchronization.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user