The Go Memory Model
Wersja z dnia 31 maja 2014 r
Wprowadzenie
modelu pamięci Go określa warunki, na których czyta zmiennej w jednym goroutine można zagwarantować przestrzeganie wartości wytwarzane przez zapis do tej samej zmiennej w innej goroutine.
Porady
programy, które modyfikują dane są jednocześnie dostępne dla wielu goroutines musi serializacji takiego dostępu.
Aby przekształcić do postaci szeregowej dostęp, chroń dane za pomocą operacji na kanale lub innych prymitywów synchronizacji , takich jak te w pakietach synchronizacji i synchronizacji/atomowej .
Jeśli musisz przeczytać resztę tego dokumentu, aby zrozumieć zachowanie swojego programu, jesteś zbyt sprytny.
Nie bądź sprytny.
synchronizacji
var a string
func hello() {
go func() { a = "hello" }()
print(a)
}
przypisanie do nie następuje po każdym przypadku synchronizacji tak nie gwarantuje się obserwować dowolną drugiej goroutine. W rzeczywistości, agresywny kompilator może usunąć całą instrukcję go.
Przyporządkowanie do i
poprzez przyrost i++
(i = i + 1
) nie jest po każdym przypadku synchronizacji, tak że nie jest zagwarantowane obserwowano przez inne goroutine. W rzeczywistości agresywny kompilator może usunąć całą instrukcję i++
.
Przykładowo
package main
import "time"
func main() {
i := 1
go func() {
for {
i++
}
}()
<-time.After(1 * time.Millisecond)
println(i)
}
wyjściowa:
1
goroutine redukuje się do:
"".main.func1 STEXT nosplit size=2 args=0x8 locals=0x0
0x0000 00000 (elide.go:7) TEXT "".main.func1(SB), NOSPLIT, $0-8
0x0000 00000 (elide.go:7) FUNCDATA $0, gclocals·2a5305abe05176240e61b8620e19a815(SB)
0x0000 00000 (elide.go:7) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (elide.go:9) JMP 0
kompilator,
for {
i++
}
może zostać zaimplementowany poprzez zwiększenie rejestru na zawsze, w zasadzie pętli no-op for
.
for { }
Po włożeniu print
oświadczenie,
package main
import "time"
func main() {
i := 1
go func() {
for {
i++
println("+1")
}
}()
<-time.After(1 * time.Millisecond)
println(i)
}
wyjściowa:
+1
+1
<<SNIP>>
+1
+1
432
goroutine rozszerza się,
"".main.func1 STEXT size=81 args=0x8 locals=0x18
0x0000 00000 (elide.go:7) TEXT "".main.func1(SB), $24-8
0x0000 00000 (elide.go:7) MOVQ (TLS), CX
0x0009 00009 (elide.go:7) CMPQ SP, 16(CX)
0x000d 00013 (elide.go:7) JLS 74
0x000f 00015 (elide.go:7) SUBQ $24, SP
0x0013 00019 (elide.go:7) MOVQ BP, 16(SP)
0x0018 00024 (elide.go:7) LEAQ 16(SP), BP
0x001d 00029 (elide.go:7) FUNCDATA $0, gclocals·a36216b97439c93dafebe03e7f0808b5(SB)
0x001d 00029 (elide.go:7) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (elide.go:8) MOVQ "".&i+32(SP), AX
0x0022 00034 (elide.go:9) INCQ (AX)
0x0025 00037 (elide.go:10) PCDATA $0, $0
0x0025 00037 (elide.go:10) CALL runtime.printlock(SB)
0x002a 00042 (elide.go:10) LEAQ go.string."+1\n"(SB), AX
0x0031 00049 (elide.go:10) MOVQ AX, (SP)
0x0035 00053 (elide.go:10) MOVQ $3, 8(SP)
0x003e 00062 (elide.go:10) PCDATA $0, $0
0x003e 00062 (elide.go:10) CALL runtime.printstring(SB)
0x0043 00067 (elide.go:10) PCDATA $0, $0
0x0043 00067 (elide.go:10) CALL runtime.printunlock(SB)
0x0048 00072 (elide.go:9) JMP 29
0x004a 00074 (elide.go:9) NOP
0x004a 00074 (elide.go:7) PCDATA $0, $-1
0x004a 00074 (elide.go:7) CALL runtime.morestack_noctxt(SB)
0x004f 00079 (elide.go:7) JMP 0
Zwiększona złożoność Goro utine oznacza, że kompilator nie rozważa już dedykowania rejestru do wartości i
. Wartość in-memory wynosząca i
jest zwiększana, co powoduje, że aktualizacje są widoczne w wyścigu danych, do goryntiny main
.
==================
WARNING: DATA RACE
Read at 0x00c420094000 by
main goroutine:
main.main()
/home/peter/gopath/src/lucky.go:14 +0xac
Previous write at 0x00c420094000 by
goroutine 5:
main.main.func1()
/home/peter/gopath/src/lucky.go:9 +0x4e
Goroutine 5 (running) created at:
main.main()
/home/peter/gopath/src/lucky.go:7 +0x7a
==================
Dla oczekiwanego wyniku, dodać trochę synchronizację,
package main
import (
"sync"
"time"
)
func main() {
mx := new(sync.Mutex)
i := 1
go func() {
for {
mx.Lock()
i++
mx.Unlock()
}
}()
<-time.After(1 * time.Second)
mx.Lock()
println(i)
mx.Unlock()
}
wyjściowa:
41807838
Program nie drukuje wartość oczekiwaną, ponieważ nie jest to rasa dane o zmiennej 'I'. –