The Go Memory Model (go memory model)

[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/atomicthose 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-beforeorder 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 hellowill print "hello, world" (perhaps at some point in the future helloafter 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 printbefore.

channel occurs before closing passage is closed due to a zero value returns received

In the previous example, by close(c)replacing c <- 0the 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 printbefore.

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 Cof a channel ktime receive operation, at the k+Ctime 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 limitthis channel to ensure a maximum of three running workfunction.

var limit = make(chan int, 3)

func main() {
    for _, w := range work {
        go func(w func()) {
            limit <- 1
            w()
            <-limit
        }(w)
    }
    select{}
}

Locks

syncPackage implements two types of locks, sync.Mutexand sync.RWMutex.

For any sync.Mutexor sync.RWMutextypes 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 printhappened 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.RWMutextypes of variables lof 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

syncOnce 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 printdone 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 twoprintprogram 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 doprintobserved donewrite operation means that the same can be observed for awrite 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 mainobserved donethe write operation means that the same can be observed for athe write operation, so this program can also print out an empty string. Even worse, no guarantee maincan be observed for donewrite operations, because there is no synchronization event between the two threads. mainThe 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 mainobserved g != niland exit the loop, we can not guarantee that it will observe the g.msginitialized value.

In all these examples, the solution is the same: use explicit synchronization.

Guess you like

Origin www.cnblogs.com/maoqide/p/11258521.html