[Translation] https://golang.google.cn/ref/mem
Go memory model specifies a condition under which, in a variable can be guaranteed goroutine read, it is possible to obtain a different value of the write to the same variable goroutine generated.
Introduction
Go memory model specifies a condition under which, in a variable can be guaranteed goroutine read, it is possible to obtain a different value of the write to the same variable goroutine generated.
Advice
If a program is to be modified simultaneously accessing a plurality goroutine data, such access must be serialized.
To serialize access channel operation or use other synchronization primitives (e.g. sync
, and sync/atomic
those packages in) to protect the data.
If you must read the rest of this document to understand the behavior of the program, then you are too smart.
Do not smart.
Happens Before
Single goroutine, the read and write must behave as if their execution order specified by the program. That is, only when reordering is not changed goroutine behavior defined in the specification language, compiler and processor can reorder the read and write operations are performed in a single goroutine. Due to this reordering, a goroutine observed order of execution may differ from the order of another goroutine perceived. For example, if a goroutine performed a = 1; b = 2;
, another goroutine may be observed before the update value b is a value of the update.
To read and write the specified requirements, we previously defined (hanppen before) occurs, the local sequence Go program memory operations. If the event e1 before (hanppen before) event e2 occurs, then we say that after the events e2 (hanppen after) e1 event occurs. In addition, if e1 and e2 did not happen did not happen until after e2, then we say e1 and e2 occur simultaneously.
Single goroutine, the happens-before
order is the order expression procedure.
If the read operation observed r both of the following are true, it allows the write operation to the variable v is written to a value v w:
1. does not occur before the w r.
2 but no other write w 'after prior w r.
In order to ensure the operation of reading of the variable v r values observed specific write operation writes v to w, w is allowed to ensure that only a read operation to a write operation r observed. That is, if the following conditions are true, in order to ensure r read operations to write operations can be observed w:
1. w occurs before r.
2. Any other shared variables v write operation occurs before or w, or occurs after r.
This set of conditions more stringent than the first group. It requires no other written simultaneously w or r.
Single goroutine, no complicated, these two definitions are equivalent: a read operation of observations r latest writing operation is written v, w.
Type of variable v of v have the value zero is written to initialize the performance of more storage model.
For reading and writing a value larger than a single machine word operations, the performance of the machine word size of a plurality of unspecified sequence of operations.
Synchronization
Initialization
Initialization programs running on a single goroutine but the goroutine may create additional goroutine run concurrently.
If the packet p imported packages q, then q init function is completed prior to any start code package p.
Function main.main started after the completion of all the init function.
Goroutine creation
Start a new goroutine the go statement occurs before the goroutine started.
For example, in this program:
var a string
func f() {
print(a)
}
func hello() {
a = "hello, world"
go f()
}
Calls hello
will print "hello, world" (perhaps at some point in the future hello
after the return).
Goroutine destruction
goroutine exit no guarantee that occurs before any of the events program. For example, in this program:
var a string
func hello() {
go func() { a = "hello" }()
print(a)
}
The assignment of a not accompanied by any synchronization event, there is no guarantee any other goroutine can observe it. In fact, a aggressive compiler might delete the entire go statement.
If a further goroutine goroutine effects must be observed that the synchronization mechanism to use locks or the like to establish a communication channel relative order.
Channel communication
the communication channel is the main method of synchronization between goroutine. Each send operation on a particular channel corresponds to the channel to match the receive operation, usually in different goroutine.
The send channel occurs before the completion of the operation of the respective receive channel
The sample program:
var c = make(chan int, 10)
var a string
func f() {
a = "hello, world"
c <- 0
}
func main() {
go f()
<-c
print(a)
}
Ensure print out "hello, world". A write occurs before c, send, receive completion i.e. before the corresponding c occurs, i.e., occurs print
before.
channel occurs before closing passage is closed due to a zero value returns received
In the previous example, by close(c)
replacing c <- 0
the program will produce the same behavior with a guaranteed.
Of unbuffered receive channel occurs before the completion of the channel of the send operation.
Sample program (the same as above, but the exchange of statements and send and receive using unbuffered channel):
var c = make(chan int)
var a string
func f() {
a = "hello, world"
<-c
}
func main() {
go f()
c <- 0
print(a)
}
Also ensure print out "hello, world". A write occurs prior to the c receive, i.e. before the occurrence of the respective send c completed, that occurs print
before.
If the channel is buffered, (e.g., c = make (chan int, 1)), then the program will not guarantee printing "hello, world". (May print an empty string, crash or perform other operations.)
Having a capacity C
of a channel k
time receive operation, at the k+C
time before the completion of the send operation.
This rule summarizes the previous channel of a buffer rules. It allows the semaphore with the channel buffer established count: the number of data channel corresponding to the current number of used, channel capacity corresponding to the allowed maximum number of simultaneously transmits a data to acquire the semaphore, receiving a data released signal. This is a limitation of the number of concurrent common usage.
The program starts a goroutine to work each entry in the list, but goroutine use limit
this channel to ensure a maximum of three running work
function.
var limit = make(chan int, 3)
func main() {
for _, w := range work {
go func(w func()) {
limit <- 1
w()
<-limit
}(w)
}
select{}
}
Locks
sync
Package implements two types of locks, sync.Mutex
and sync.RWMutex
.
For any sync.Mutex
or sync.RWMutex
types of variables l
, and n <m, the n-th call l.Unock()
the m-th call l.Lock()
occurs before returning.
The sample program:
var l sync.Mutex
var a string
func f() {
a = "hello, world"
l.Unlock()
}
func main() {
l.Lock()
go f()
l.Lock()
print(a)
}
Ensure print out "hello, world". The first call l.Unlock()
(at f ()), the second call l.Lock()
occurs before returning (in main (),), that print
happened before.
For any call to l.RLock on a sync.RWMutex variable l, there is an n such that the l.RLock happens (returns) after call n to l.Unlock and the matching l.RUnlock happens before call n+1 to l.Lock.
For the sync.RWMutex
types of variables l
of l.RLock()
any call, so that this has a n l.RLock()
at the n-th call l.Unlock()
occurs after (return) corresponding to and l.RUnlock()
in the n + 1 calls l.Lock()
occur before.
Once
sync
Once through the use of package type, to provide a secure initialization mechanism in the presence of a plurality of goroutine. Multiple threads can be executed for a particular f nce.Do(f)
, but only one thread will actually run f()
, and the other calls will be blocked until the f()
return.
From the once.Do(f)
middle for f()
a single call in any of once.Do(f)
the previous call occurs (return).
In the following procedure:
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()
}
Calls twoprint()
will only call setup()
once. setup method will be print
done before. The result is "hello, world" will be printed twice.
Incorrect synchronization
Note that, the reading operation can be observed value r r w write operations occurring simultaneously written. Even if this happens, the reading operation does not mean that occur after the observed r write operation occurs before w.
The following program:
var a, b int
func f() {
a = 1
b = 2
}
func g() {
print(b)
print(a)
}
func main() {
go f()
g()
}
g may occur 2 to print and then print 0.
This makes some common useful syntax is invalid.
Double-check the lock is to avoid synchronization overhead.
For example, the twoprint
program may be erroneously written as:
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()
}
But this is no guarantee that, in the doprint
observed done
write operation means that the same can be observed for a
write operations. This version may (incorrectly) print an empty string instead of "hello, world".
Another incorrect idiom is busy waiting for a value, such as:
var a string
var done bool
func setup() {
a = "hello, world"
done = true
}
func main() {
go setup()
for !done {
}
print(a)
}
As before, no guarantee that main
observed done
the write operation means that the same can be observed for a
the write operation, so this program can also print out an empty string. Even worse, no guarantee main
can be observed for done
write operations, because there is no synchronization event between the two threads. main
The cycle can not guarantee complete.
This theme has a more subtle variations, such as this program:
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)
}
Even main
observed g != nil
and exit the loop, we can not guarantee that it will observe the g.msg
initialized value.
In all these examples, the solution is the same: use explicit synchronization.