Go's memory model

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Description

Translated from the Go Memory Model

Introduced

How to ensure that you see the values of variables modified in another goroutine in one goroutine, this article is described in detail.

Suggestions

If there are other goroutine reading at the same time when modifying data in the program, the read must be serialized. To serialize access, use channel or other synchronization primitives, such as sync and sync/atomic, to protect your data.

First occurrence

In a gouroutine, reading and writing must be performed in the order of the program. That is, compilers and processors can modify the order of execution of Read and write only if they do not alter the behavior of this goroutine. Because of the rearrangement, different goroutine may see different execution sequences. For example, one goroutine executes a = 1;b = 2; , and another goroutine may see b the a update before.
To illustrate the requirements for reading and writing, we define the 先行发生(Happens Before) partial ordering of memory operations performed in the--go program. If the event e1 occurs e2 before, we can say that it e2 happened e1 after. If e1 it doesn't happen e2 before and it doesn't happen e2 after that, we'll say e1 and e2 be concurrent.
The order in which the antecedent occurs in a separate goroutine is the order in which the program is expressed.
When the following conditions are met, the read operation R to the variable v is allowed to see the write Operation W for v :

    1. R does not occur first in W

    2. No other write operation to v before W R

To ensure that the read operation R of the variable v sees the write operation Wto v , make sure that W is the only write operation that R allows to see. That is, when the following conditions are met,R is guaranteed to see w:

    1. W first occurs in R

    2. Other writes to the shared variable v are either before W or after R .
      This pair of conditions is stricter than the previous conditions and requires no other write operations to occur with W or r concurrency.

There is no concurrency in a separate goroutine, so the above two definitions are the same: read operation R sees the last write W write v value. When multiple goroutine access shared variable v , they must use synchronous events to establish the first occurrence of this condition to ensure that read operations can see the required write operations.
The 0-value initialization of the variable v behaves the same as the write operation in the memory model.
A read-write operation on a variable greater than one word behaves like a variable with multiple word sizes in an indeterminate order.

Synchronous

Initialization

The initialization of the program is performed in a separate goroutine, but this goroutine may create additional goroutine that are executed concurrently.
if package P introduces (import) packet Q, then the end of the init function of Q takes place at the beginning of all Init functions of P
The start of the Main.main function occurs after all INIT functions have ended

Create Goroutine

goKeyword opens a new goroutine, which occurs first in this goroutine, such as the following program:

var a stringfunc f() {    print(a)}func hello() {    a = "hello, world"    go f()}

The call hello will print "Hello, world" at some point later (possibly after the hello return)

Destroying Goroutine

Gouroutine's exit does not guarantee that the antecedent occurs in any event of the program. For example, the following program:

var a stringfunc hello() {    go func() { a = "hello" }()    print(a)}

No synchronization is used to limit the assignment of a, so there is no guarantee that other goroutine can see a change. In fact, a radical compiler might erase the entire GO statement.
If you want to see the performance of another goroutine in one goroutine, use a synchronization mechanism such as a lock or channel to establish the relative order in which the program executes.

Channel Communication

Channel communication is the main method of Goroutine synchronization. Each send operation on a specific channel will match the receive operation that is normally performed on the other goroutine.
The transmit operation of the channel occurs prior to the completion of the corresponding receive Operation
For example:

var c = make(chan int, 10)var a stringfunc f() {    a = "hello, world"    c <- 0}func main() {    go f()    <-c    print(a)}

This program will ensure that "Hello, World" is printed. The writing of a first occurs on the C, the first occurs in the corresponding receive completion on C, the first occurs in print .
The closing of the channel occurs at the receipt of a zero value because the channel has been closed.
In the example above, it will be c <- 0 replaced close(c) with the same result.
the reception of the unbuffered channel occurs at the completion of the Send
The following program (similar to the above, only exchanges the read and write locations of the channel and uses the non-buffered channel):

var c = make(chan int)var a stringfunc f() {    a = "hello, world"    <-c}
func main() {    go f()    c <- 0    print(a)}

This program also ensures that "Hello, World" is printed. The first occurrence of a is received from C , the first occurs in the Send to C completed, the first occurs in print .

If the channel is buffered (for example c = make(chan int, 1) ), the program does not guarantee that "Hello, World" (which may print null characters, program crashes, or other behavior) is printed.
The first K-Receive on the channel with volume C takes place on the first k+c sent from the channel.
This rule promotes the previous rule to a buffered channel. Count semaphores can be achieved by buffering the channel: the number of elements in the channel corresponds to the number of activities, the capacity of the channel represents the maximum number of simultaneous activities, the sending element acquires the semaphore, and the receiving element releases the semaphore, which is the usual use of limiting concurrency.
The following program work opens a goroutine for each item in, but these goroutine ensure that up to three working functions (W) are executed at the same time through a restricted channel.

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

Lock

The sync Pack implements data type sync for two locks. Mutexes and Sync.rwmutex.
to any of the sync. Mutex or Sync.rwmutex variable L and N < m,n call L. Unlock () first occurs at M-Times L. Lock () returns
The following procedures:

var l sync.Mutexvar a stringfunc f() {    a = "hello, world"    l.Unlock()}func main() {    l.Lock()    go f()    l.Lock()    print(a)}

can guarantee to print out "Hello, World". First Call L. Unlock () (in F ()) takes precedence over the second L in Main. Lock () returns, first occurs in print.
For sync. Rwmutex variable L, arbitrary function call L. The rlock satisfies the nth l.rlock and occurs at the nth call L.unlock, and the corresponding L.runlock first occurs in the N+1 call L. Lock.

Once

The Sync Package Once provides a secure initialization mechanism for multiple goroutine. Can be executed in multiple threads once.Do(f) , but only one f() will execute, and other calls will block until f() returned.
Returns by once.Do(f) performing f() an antecedent (meaning F () return) on the other once.Do(f) .
The following programs:

var a stringvar once sync.Oncefunc setup() {    a = "hello, world"}func doprint() {    once.Do(setup)    print(a)}func twoprint() {    go doprint()    go doprint()}

twoprintthe call will print "Hello, World" two times. setupexecutes only on the first time doprint .

Incorrect synchronization method

Note that read operation R may see concurrent write Operation W. Even this does not indicate that the read after R can see the write before W .
The following programs:

var a, b intfunc f() {    a = 1    b = 2}func g() {    print(b)    print(a)}func main() {    go f()    g()}

gIt is possible to print out 2 and then 0.
This fact proves that some old habits are wrong.
Double-check locking is done to avoid synchronization of resource consumption. For example, the Twoprint program may be incorrectly 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()}

The assignment is not guaranteed to be visible in the view of the doprint done assigned value a . This program may incorrectly output NULL characters instead of "Hello, World".
Another bad habit is to be busy waiting for example:

var a stringvar done boolfunc setup() {    a = "hello, world"    done = true}func main() {    go setup()    for !done {    }    print(a)}

Similar to the previous program, it is done a possible for this program to print out null characters if it is not guaranteed to see the assigned value in main. Even worse, because there are no synchronization events between the two threads, the assignment may never be seen in main done , so the loop in main is not guaranteed to end.
Make a minor change to the 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 sees g! = Nil and exits the loop, it is not guaranteed to see the initialization value of g.msg.
in all of the above examples, the solution is the same: use synchronization explicitly.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.