[Translate] go memory model

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

Introduction

The Go memory model specifies the conditions under which reads of a variable in one goroutine can is guaranteed to observe Values produced by writes to the same variable in a different goroutine.

The Go memory model qualifies conditions to satisfy these conditions in order for the variable to be securely read and written between different goroutine

Happens before

Within a single goroutine, reads and writes must behave as if they executed in the order specified by the program. That's, compilers and processors may reorder the reads and writes executed within a single goroutine only when the Reorde Ring does not a change of the behavior within that goroutine as defined by the language specification. Because of this reordering, the execution order observed by one goroutine could differ from the order perceived by another. For example, if one goroutine executes a=1; b=2; , another might observe the updated value of B before the updated value of a.

A = 1; b = 2;

If there is only one goroutine read and write behavior and their position in the program is the same, the compiler and the processor can only be used when reading and writing in the program to ensure the correct behavior, such as reading and writing order can be optimized for example, in a goroutine execution Another goroutine may be aware of the update operation of B prior to a

To specify the requirements of reads and writes, we define happensbefore, a partial order on the execution of me Mory Operations in a Go program. If Event E1happens before event E2, then we say the E2 happens after E1. Also, if E1 does not happen before E2 and does not happen after E2, then we say that E1 and E2happen concurrently.

In order to define the requirements for read and write we define a partial order for the memory operation of the GO program "occurs before X" If the time E1 occurs before E2 then we think E2 event appears after E1 same if E1 does not appear before E2 and does not appear after E2 then we think E1 and E2 are parallel

Within a single goroutine, the Happens-before order was the order expressed by the program.

In the same goroutine, the order in which "before X" is expressed in the sequence is consistent.

A read R of a variable v is allowed to observe a write W to V if both of the following hold:

Write to V W if you want to be seen by V's read operation R, you need to meet the following two conditions:

    1. R does not happen before W. R does not occur before W
    2. There is no other write W ' to V so happens after W but before R. No other write Operation W ' occurs before W R

To guarantee. A read R of a variable v observes a particular write W to V, ensure. W is The only write R was allowed to observe. That's, R is guaranteed to observe W if both of the following hold:

To ensure that the read operation R can observe that a specific W write operation needs to ensure that W is the only observed write operation of R that is, only if the following conditions are satisfied, the R will be observed W

    1. W happens before R. W occurs before R
    2. Any and write to the shared variable v either happens before W or after R. Any other write to V either happens before W or after R

This pair of conditions is stronger than the first pair; It requires that there is no other writes happening concurrently with W or R.

These two conditions are more stringent than the previous two conditions, which requires no other write operations and W or R is parallel.

Within a single goroutine, there was no concurrency, so the and the definitions is equivalent:a read R observes the Value written by the most recent write W to V. When multiple goroutines access a shared variable V, they must use synchronization events to establish Happens-before cond Itions that ensure reads observe the desired writes.

There is no parallel argument in a single goroutine environment so the above two pairs of conditions are the same read operation observes the last write to the variable v when multiple Goroutine concurrently access the shared variable v they need to use synchronous events to establish the "before X" condition to ensure that the read can be observed to write

The initialization of variable v with the zero value for V ' s type behaves as a write in the memory model.

The V variable uses the corresponding type of 0 value initialization behavior in the memory model and the write variable V is similar

Reads and writes of values larger than a single machine word behave as multiple machine-word-sized operations in an unspec ified order.

If the read-write value is greater than a machine word, their behavior and multi-machine word operations are similar in order to indeterminate


Synchronization

Initialization

Program initialization runs with a single goroutine, but that Goroutine could create other goroutines, which run concurrently.

Program initialization is performed in a single goroutine environment, but this goroutine can be created in parallel with other goroutine.

If A package P imports package Q, the completion of Q's Init functions happens before the start of any of P ' s.

If the package P imports the INIT function of the packet Q Q before the initialization of Q is performed

The start of the function Main.main happens after all init functions has finished.

The Main.main function does not execute until all initialization function Init has ended


Goroutine creation

The GO statement that starts a new goroutine happens before the Goroutine ' s execution begins.

The GO statement opens a new Goroutine and is created before the Goroutine executes.

For example, the This program:

For example:

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

Calling Hello would print "Hello, world" at some-in-the-the-perhaps after hello have returned.

Calling the Hello function on an occasion prints "Hello,world" (possibly in case the Hello function has returned)


Goroutine Destruction

The exit of a goroutine is not guaranteed to happen before all event in the program. For example, the This program:

We're not sure goroutine. In the order of exit, for example, before event E, it must exit For example:

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

The assignment to a isn't followed by any synchronization event, so it's not guaranteed to being observed by any other Goro Utine. In fact, an aggressive compiler might delete the entire GO statement.

The assignment of this code a above does not follow any synchronization events, so it is not guaranteed that it will be observed by other goroutine the fact that the compiler will delete the code (go func () {A=FASD})

If the effects of a goroutine must be observed by another goroutine, use a synchronization mechanism such as a lock or Cha Nnel communication to establish a relative ordering.

If you want to observe variable update operations between different goroutine, then you need to use some synchronous mechanisms such as using locks or communicating via channel to establish relative reading and writing order.


Channel Communication

Channel communication is the main method of synchronization between Goroutines. Each send on a particular channel are matched to a corresponding receive from the channel, usually in a different goroutin E.

Communication using channel is the primary method of synchronizing between different goroutine each send operation to a specific channel typically corresponds to a receive operation in another Goroutine

A Send on a channel happens before the corresponding receive from that channel completes.

The send operation of the channel must occur before the corresponding receive operation is completed

This program: The following code:

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 in C, which happens before the corresponding receive on C completes, which happens Before the print.

You can ensure that the print "Hello, world" write operation occurs before the send operation of channel C before send takes place before the receive operation in C before receive occurs before print

The closing of a channel happens before a receive that returns a zero value because the channel is closed.

The closing channel occurs before acceptance because the channel has been shut down in Mai, go f () opens another goroutine in main, then executes to <-c and then the channel receives this blocking Goroutine F () to start After the line write variable a closes the channel back to main print a channel here is purely for synchronization

In the previous example, replacing C <-0 with close (c) yields a program with the same guaranteed behavior.

If you replace c<-0 with close (c) in the above code, it behaves the same way.

A receive from A unbuffered channel happens before the send to that channel completes.

The receive operation from the Unbuffered channel occurs before the end of the send operation of the channel

This program (as above, but with the send and receive statements swapped and using a unbuffered channel):

The following code is the same as the above code but the receive and send operation is dropped and a unbuffered channel is used:

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". The write to a happens before the receive in C, which happens before the corresponding Send on C completes, which happens Before the print.

This code also guarantees that the write of "Hello World" to a writes occurs before the receive operation of channel C takes place before the corresponding send operation is completed and the send operation is sent before printing

If the channel were buffered (e.g., C = Make (chan int, 1) and then the program would isn't being guaranteed to print "Hello, world ". (It might print the empty string, crash, or do something else.)

If there is a buffer channel, then the code above will not guarantee that "Hello World" will be printed.


Locks

The sync package implements the data types, sync. Mutex and sync. Rwmutex.

The sync pack implements two types of lock sync. Mutexes and Sync.rwmutex

For any sync. Mutex or sync. Rwmutex variable L and n < m, call n of L.unlock () happens before call m of L.lock ( ) returns.

To any of the sync. Mutex or Sync.rwmutex variable l n <m, nth call l.unlock occurs on M call L. Lock back before

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 L.unlock () (in F) happens before the second call to L.lock () (in main) returns, which happens before the Print.

The above code guarantees that the output "Hello,world" is called the first time L. Unlock occurs at the second invocation of L. Lock prior to two calls I.lock occurs before the print operation

For any call to L.rlock on a sync. Rwmutex variable L, there is a n such, the L.rlock happens (returns) after Call n to L.unlock and T He matching L.runlock happens before call n+1 to L.lock.

At sync. Any l.rlock operation on the Rwmutex variable l will present an n l.rlcok occurs after the nth l.unlock is called. Runlock occurred at N+1 L. Lock Call before


Once

The sync package provides a safe mechanism for initialization in the presence of multiple goroutines through the use of th e Once type. Multiple threads can execute once. Do (f) for a particular F, if only one would run F (), and the other calls block until F () have returned.

The Sync Pack provides a secure mechanism for initializing in multi-goroutine environments requiring the once type of the sync pack multiple threads can execute once simultaneously. Do (f) but only one thread will call F () the other threads will block until F () returns

A single Call of F () from once. Do (f) happens (returns) before any call of once. Do (f) returns.

Through once. Do (f) Call F takes place in any other once. Do (f) before returning

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 "Hello, world" to be printed twice. The first call to Twoprint runs Setup once.

Call two times Twoprint will output two times "Hello,world"


Incorrect synchronization

Note that a read R is observe the value written by a write W , happens concurrently with R. Even if this occurs, it does not imply that reads happening after R would observe writes that happened before w.

Note that read operation R may observe a write operation with R parallel W even if such a situation does not mean that the read after R will observe the write operation 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 2 and then 0.

The above code might output 2 and then output 0.

This fact invalidates a few common idioms. This fact makes a lot of things obsolete.

Double-checked locking is a attempt to avoid the overhead of synchronization. For example, the Twoprint program might is incorrectly written as:

One way to avoid synchronization problems is to check the lock more once. For example: The Twoprint program may be written as follows:

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, in Doprint, observing the write-to-do implies observing the write to a. This version can (incorrectly) print a empty string instead of "Hello, World".

But this writing does not guarantee that the write to a can be observed in the doprint.

Another incorrect idiom is busy waiting for a value, as in:

Another way to use the error is to be busy waiting for a variable value:

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 to do implies observing the write to a, so this prog Ram could print an empty string too. Worse, there is no guarantee then the write to done would ever be observed by main, since there was no synchronization even TS between the threads. The loop in main isn't guaranteed to finish.

The result of this writing is the same as above, this code might just print an empty string. Even worse, it's not guaranteed that the write to done will be observed in main because there's no synchronization between them. The loop in main is not guaranteed to wait for the desired value.

There is subtler variants on this theme, such as this program.

There are some variants of the above code:

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 g! = nil and exits its loop, there are no guarantee that it'll observe the initialized value for G. Msg.

Same effect

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


Look not very clear ah ...

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.