Die Ausgabe ist immer 1
.
Allerdings ist es absolut ausreichend, dass 1s genug ist, damit die for
-Schleife viele Male durchlaufen kann.
Ich denke, die i
in der Schließung ist die i
in der main
func.
Siehe den folgenden Code.
%Vor%Nach vielen Zeilen von "+1" ist die Ausgabe genau wie erwartet eine große Zahl.
Version vom 31. Mai 2014
Einführung
Das Go-Speichermodell gibt die Bedingungen an, unter denen a Variable in einer Goroutine kann garantiert werden, produzierte Werte zu beobachten durch schreibt auf die gleiche Variable in einer anderen Goroutine.
Hinweis
Programme, die Daten modifizieren, auf die gleichzeitig mehrfach zugegriffen wird Göroutinen müssen diesen Zugang serialisieren.
Um den Zugriff zu serialisieren, schützen Sie die Daten mit Kanaloperationen oder anderem Synchronization Primitive wie diejenigen in der Synchronisation und Sync / atomaren Pakete.
Wenn Sie den Rest dieses Dokuments lesen müssen, um das Verhalten zu verstehen von deinem Programm bist du zu schlau.
Sei nicht schlau.
Synchronisation
%Vor%Auf die Zuweisung zu a folgt kein Synchronisationsereignis es ist nicht garantiert, dass es von einer anderen Goroutine beobachtet wird. Eigentlich, Ein aggressiver Compiler könnte die gesamte go-Anweisung löschen.
Der Zuweisung an i
, über inkrement i++
( i = i + 1
), folgt kein Synchronisierungsereignis, sodass nicht garantiert ist, dass es von einer anderen Goroutine beobachtet wird. In der Tat könnte ein aggressiver Compiler die gesamte i++
-Anweisung löschen.
Zum Beispiel
%Vor%Ausgabe:
%Vor%Die Goroutine reduziert sich auf:
%Vor%Für den Compiler
%Vor% kann implementiert werden, indem ein Register für immer inkrementiert wird, im Wesentlichen eine no-op for
-Schleife.
Nach dem Einfügen einer print
-Anweisung,
Ausgabe:
%Vor%Die Goroutine erweitert sich zu
%Vor% Die erhöhte Komplexität der Goroutine bedeutet, dass der Compiler nicht länger daran denkt, ein Register dem Wert von i
zu widmen. Der In-Memory-Wert von i
wird erhöht, wodurch die Aktualisierungen mit einem Datenrennen für die main
-Goroutine sichtbar werden.
Fügen Sie für das erwartete Ergebnis eine Synchronisierung hinzu,
%Vor%Ausgabe:
%Vor% Der gleichzeitige Zugriff auf die Variable i
muss synchronisiert werden:
Synchronisation ist besser mit Kanälen oder den Einrichtungen der Paket synchronisieren Speicher teilen durch Kommunikation; kommuniziere nicht mit Speicher teilen.
Hinweis: Ссылка
Das ist interessant, also teile ich meine Experimente:
0- Ihr Code mit time.Sleep(1 * time.Second)
( nicht empfohlen - nicht synchronisiert ):
Ausgabe:
%Vor% 1- Verwendung von i := new(int)
( nicht empfohlen - nicht synchronisiert ):
Ausgabe (CPU: i7-7700K @ 4,2 GHz):
%Vor% 2- Synchronisieren mit atomic.AddInt64(&i, 1)
( Paket atomic bietet grundlegende atomare Speichergrundelemente niedriger Ebene, die für die Implementierung von Synchronisationsalgorithmen nützlich sind ):
Ausgabe:
%Vor% 3- Synchronisieren mit chan int
:
Ausgabe:
%Vor% 4- Synchronisation mit sync.WaitGroup
:
Ausgabe:
%Vor%5- Synchronisieren mit quit-Kanal:
%Vor%Ausgabe:
%Vor% 6- Synchronisation mit sync.RWMutex
:
Ausgabe:
%Vor%verwandte Themen:
Das Go-Speichermodell
Es gibt keine Entsprechung zu volatilen und registrieren in Go
Unterstützt Go volatile / nichtflüchtige Variablen?