Inhaltsverzeichnis
Die Rolle von „passiert vor“" >Die Rolle von „passiert vor“
Was passiert vorher?" >Was passiert vorher?
An diesem Punkt sollten Sie verstehen, warum der zweite Satz von Bedingungen strenger ist als der erste Satz von Bedingungen. Unter dem ersten Satz von Bedingungen darf w beobachtet werden, und im zweiten Satz ist die Einhaltung von w garantiert. " >An diesem Punkt sollten Sie verstehen, warum der zweite Satz von Bedingungen strenger ist als der erste Satz von Bedingungen. Unter dem ersten Satz von Bedingungen darf w beobachtet werden, und im zweiten Satz ist die Einhaltung von w garantiert.
Go的初始化" >Go的初始化
goroutine的创建结束" >goroutine的创建结束
Channel的发送和接收" >Channel的发送和接收
Lock" >Lock
Once" >Once
不正确的同步" >不正确的同步
Heim Backend-Entwicklung Golang Vernacular Go-Speichermodell Happen-Before

Vernacular Go-Speichermodell Happen-Before

Jul 24, 2023 pm 03:47 PM
go 内存模型

Wenn mehrere Goroutinen gleichzeitig auf dieselben Daten zugreifen, müssen die gleichzeitigen Zugriffsvorgänge serialisiert werden. Die Serialisierung von Lese- und Schreibvorgängen in Go kann durch Kanalkommunikation oder andere Synchronisierungsprimitive (wie Mutex-Sperren, Lese-/Schreibsperren im Synchronisierungspaket und atomare Operationen in sync/atomic) gewährleistet werden.

Passiert vorher

In einer einzelnen Goroutine muss das Lese- und Schreibverhalten mit der vom Programm festgelegten Ausführungsreihenfolge übereinstimmen. Mit anderen Worten: Compiler und Prozessoren können Anweisungen in einer einzelnen Goroutine neu anordnen, ohne das durch die Sprachspezifikation definierte Verhalten zu ändern.

a := 1
b := 2
Nach dem Login kopieren

Aufgrund der Neuordnung der Direktive, b := 2 kann vor a := 1Ausgeführt. In einer einzelnen Goroutine hat die Anpassung der Ausführungsreihenfolge keinen Einfluss auf das Endergebnis. In mehreren Goroutine-Szenarien können jedoch Probleme auftreten. b := 2可能先于a := 1执行。单goroutine中,该执行顺序的调整并不会影响最终结果。但多个goroutine场景下可能就会出现问题。

var a, b int
// goroutine A
go func() {
    a := 5
    b := 1
}()
// goroutine B
go func() {
    for b == 1 {}
    fmt.Println(a)
}()
Nach dem Login kopieren

执行上述代码时,预期goroutine B能够正常输出5,但因为指令重排序,b := 1可能先于a := 5

func sleep() bool {
   time.Sleep(time.Second)
   return true
}

go fmt.Println(sleep())
Nach dem Login kopieren
Nach dem Login kopieren

Beim Ausführen des obigen Codes wird erwartet, dass Goroutine B normalerweise 5 ausgibt, aber aufgrund der Neuordnung der Anweisungen b := 1 kann vor a := 5 wird ausgeführt und Goroutine B gibt möglicherweise schließlich 0 aus. 🎜
"

Hinweis: Das obige Beispiel ist ein falsches Beispiel und dient nur zur Veranschaulichung.

"

Um die Anforderungen für Lese- und Schreibvorgänge zu verdeutlichen, hat Go happens before eingeführt, eine Methode zur Ausführung Speicheroperationen.

Die Rolle von „passiert vor“

Wenn mehrere Goroutinen auf gemeinsam genutzte Variablen zugreifen, müssen sie Synchronisierungsereignisse einrichten, um die „passiert vor“-Bedingung sicherzustellen, um sicherzustellen, dass Lesevorgänge erwartete Schreibvorgänge beobachten können.

Was passiert vorher?

Wenn Ereignis e1 vor Ereignis e2 auftritt, dann sagen wir, dass e2 nach e1 auftritt. Wenn e1 weder vor e2 noch nach e2 auftritt, dann sagen wir, dass e1 und e2 gleichzeitig auftreten.

In einer einzelnen Goroutine ist die Reihenfolge der Vorgänge die Reihenfolge der Programmausführung. Wie ist also die Reihenfolge des Vorhergehens? Schauen wir uns die Bedingungen unten an.

Wenn die Leseoperation r und die Schreiboperation w für eine Variable v die folgenden beiden Bedingungen erfüllen, ist r erlaubt, w zu beobachten:

  1. r tritt nicht vor w auf.
  2. Nach w und vor r findet kein weiterer Schreibvorgang statt.

Um sicherzustellen, dass ein Lesevorgang r der Variablen v einen bestimmten Schreibvorgang w beobachten kann, muss sichergestellt werden, dass w der einzige Schreibvorgang ist, der von r beobachtet werden darf. Wenn dann r und w beide die folgenden Bedingungen erfüllen, kann r sicherstellen, dass w beobachtet wird:

    w tritt vor r auf.
  1. Andere Schreibvorgänge erfolgen vor w und nach r.
  2. Es gibt keine Parallelität in einer einzelnen Goroutine, diese beiden Bedingungen sind gleichwertig. Lao Xu erweiterte diese Grundlage und stellte fest, dass diese beiden Bedingungen für eine Single-Core-Betriebsumgebung gleichermaßen gleichwertig sind. Im Fall der Parallelität ist der letzte Satz von Bedingungen strenger als der erste Satz.

Wenn Sie verwirrt sind, haben Sie Recht! Auch Lao Xu war zunächst verwirrt. Diese beiden Bedingungen waren gleich. Aus diesem Grund verglich Lao Xu ihn wiederholt mit dem Originaltext, um sicherzustellen, dass das obige Verständnis korrekt war.

Vernacular Go-Speichermodell Happen-Before

Lasst uns unser Denken ändern und umgekehrt argumentieren. Wenn die beiden Bedingungen gleich sind, ist es nicht nötig, den Originaltext zweimal zu schreiben. Natürlich ist die Sache nicht einfach.

Vernacular Go-Speichermodell Happen-Before

Bevor ich mit der Analyse fortfahre, möchte ich mich bei meinem Chinesischlehrer bedanken. Ohne Sie wäre ich nicht in der Lage, ihre Unterschiede zu entdecken.

r没有发生在w之前,则r可能的情况是r发生在w之后或者和w同时发生,如下图(实心表示可同时)。

Vernacular Go-Speichermodell Happen-Before

没有其他写操作发生在w之后和r之前

Vernacular Go-Speichermodell Happen-Before
Vernacular Go-Speichermodell Happen-BeforeNach w und vor r findet kein weiterer Schreibvorgang statt, dann andere Schreibvorgänge w' kann vor w oder gleichzeitig mit w auftreten, oder es kann nach r oder gleichzeitig mit r auftreten, wie in der Abbildung unten gezeigt (durchgezogen bedeutet, dass es gleichzeitig auftreten kann).

Vernacular Go-Speichermodell Happen-BeforeDer zweite Satz von Bedingungen ist sehr klar, da w vor r auftritt und andere Schreibvorgänge nur vor w oder nach r erfolgen können, wie in der Abbildung unten gezeigt (die Leerzeichen zeigen an, dass sie nicht gleichzeitig erfolgen können). ).

An diesem Punkt sollten Sie verstehen, warum der zweite Satz von Bedingungen strenger ist als der erste Satz von Bedingungen. Unter dem ersten Satz von Bedingungen darf w beobachtet werden, und im zweiten Satz ist die Einhaltung von w garantiert.

Synchronisation in Go

🎜🎜Im Folgenden sind einige in Go vereinbarte Synchronisationsereignisse aufgeführt. Sie können sicherstellen, dass das Programm dem Prinzip „Vorher geschieht“ folgt, sodass gleichzeitige Goroutinen relativ ordnungsgemäß ablaufen. 🎜

Go的初始化

程序初始化运行在单个goroutine中,但是该goroutine可以创建其他并发运行的goroutine。

如果包p导入了包q,则q包init函数执行结束先于p包init函数的执行。main函数的执行发生在所有init函数执行完成之后。

goroutine的创建结束

goroutine的创建先于goroutine的执行。老许觉得这基本就是废话,但事情总是没有那么简单,其隐含之意大概是goroutine的创建是阻塞的。

func sleep() bool {
   time.Sleep(time.Second)
   return true
}

go fmt.Println(sleep())
Nach dem Login kopieren
Nach dem Login kopieren

上述代码会阻塞主goroutine一秒,然后才创建子goroutine。

goroutine的退出是无法预测的。如果用一个goroutine观察另一个goroutine,请使用锁或者Channel来保证相对有序。

Channel的发送和接收

Channel通信是goroutine之间同步的主要方式。

  • Channel的发送动作先于相应的接受动作完成之前。

  • 无缓冲Channel的接受先于该Channel上的发送完成之前。

这两点总结起来分别是开始发送开始接受发送完成接受完成四个动作,其时序关系如下。

开始发送 > 接受完成
开始接受 > 发送完成
Nach dem Login kopieren

注意:开始发送和开始接受并无明确的先后关系

  • Channel的关闭发生在由于通道关闭而返回零值接受之前。

  • 容量为C的Channel第k个接受先于该Channel上的第k+C个发送完成之前。

这里使用极限法应该更加易于理解,如果C为0,k为1则其含义和无缓冲Channel的一致。

Lock

对于任何sync.Mutex或sync.RWMutex变量l以及n < m,第n次l.Unlock()的调用先于第m次l.Lock()的调用返回。

假设n为1,m为2,则第二次调用l.Lock()返回前一定要先调用l.UnLock()。

对于sync.RWMutex的变量l存在这样一个n,使得l.RLock()的调用返回在第n次l.Unlock()之后发生,而与之匹配的l.RUnlock()发生在第n + 1次l.Lock()之前。

不得不说,上面这句话简直不是人能理解的。老许将其翻译成人话:

有写锁时:l.RLock()的调用返回发生在l.Unlock()之后。

有读锁时:l.RUnlock()的调用发生在l.Lock()之前。

注意:调用l.RUnlock()前不调用l.RLock()和调用l.Unlock()前不调用l.Lock()会引起panic。

Once

once.Do(f)中f的返回先于任意其他once.Do的返回。

不正确的同步

错误示范一

var a, b int

func f() {
 a = 1
 b = 2
}

func g() {
 print(b)
 print(a)
}

func main() {
 go f()
 g()
}
Nach dem Login kopieren

这个例子看起来挺简单,但是老许相信大部分人应该会忽略指令重排序引起的异常输出。假如goroutine f指令重排序后,b=2先于a=1发生,此时主goroutine观察到b发生变化而未观察到a变化,因此有可能输出20

老许在本地实验了多次结果都是输出0020这个输出估计只活在理论之中了。

错误示范二

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()
}
Nach dem Login kopieren

这种双重检测本意是为了避免同步的开销,但是依旧有可能打印出空字符串而不是“hello, world”。说实话老许自己都不敢保证以前没有写过这样的代码。现在唯一能想到的场景就是其中一个goroutine doprint执行到done = true(指令重排序导致done=true先于a="hello, world"执行)时,另一个goroutine doprint刚开始执行并观察到done的值为true从而打印空字符串。

Das obige ist der detaillierte Inhalt vonVernacular Go-Speichermodell Happen-Before. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Chat -Befehle und wie man sie benutzt
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Vertiefendes Verständnis des Golang-Funktionslebenszyklus und des Variablenumfangs Vertiefendes Verständnis des Golang-Funktionslebenszyklus und des Variablenumfangs Apr 19, 2024 am 11:42 AM

In Go umfasst der Funktionslebenszyklus Definition, Laden, Verknüpfen, Initialisieren, Aufrufen und Zurückgeben; der Variablenbereich ist in Funktionsebene und Blockebene unterteilt. Variablen innerhalb einer Funktion sind intern sichtbar, während Variablen innerhalb eines Blocks nur innerhalb des Blocks sichtbar sind .

Wie kann ich Zeitstempel mithilfe regulärer Ausdrücke in Go abgleichen? Wie kann ich Zeitstempel mithilfe regulärer Ausdrücke in Go abgleichen? Jun 02, 2024 am 09:00 AM

In Go können Sie reguläre Ausdrücke verwenden, um Zeitstempel abzugleichen: Kompilieren Sie eine Zeichenfolge mit regulären Ausdrücken, z. B. die, die zum Abgleich von ISO8601-Zeitstempeln verwendet wird: ^\d{4}-\d{2}-\d{2}T \d{ 2}:\d{2}:\d{2}(\.\d+)?(Z|[+-][0-9]{2}:[0-9]{2})$ . Verwenden Sie die Funktion regexp.MatchString, um zu überprüfen, ob eine Zeichenfolge mit einem regulären Ausdruck übereinstimmt.

Wie sende ich Go WebSocket-Nachrichten? Wie sende ich Go WebSocket-Nachrichten? Jun 03, 2024 pm 04:53 PM

In Go können WebSocket-Nachrichten mit dem Paket gorilla/websocket gesendet werden. Konkrete Schritte: Stellen Sie eine WebSocket-Verbindung her. Senden Sie eine Textnachricht: Rufen Sie WriteMessage(websocket.TextMessage,[]byte("message")) auf. Senden Sie eine binäre Nachricht: Rufen Sie WriteMessage(websocket.BinaryMessage,[]byte{1,2,3}) auf.

Der Unterschied zwischen Golang und Go-Sprache Der Unterschied zwischen Golang und Go-Sprache May 31, 2024 pm 08:10 PM

Go und die Go-Sprache sind unterschiedliche Einheiten mit unterschiedlichen Eigenschaften. Go (auch bekannt als Golang) ist bekannt für seine Parallelität, schnelle Kompilierungsgeschwindigkeit, Speicherverwaltung und plattformübergreifende Vorteile. Zu den Nachteilen der Go-Sprache gehören ein weniger umfangreiches Ökosystem als andere Sprachen, eine strengere Syntax und das Fehlen dynamischer Typisierung.

Wie vermeidet man Speicherlecks bei der technischen Leistungsoptimierung von Golang? Wie vermeidet man Speicherlecks bei der technischen Leistungsoptimierung von Golang? Jun 04, 2024 pm 12:27 PM

Speicherlecks können dazu führen, dass der Speicher des Go-Programms kontinuierlich zunimmt, indem: Ressourcen geschlossen werden, die nicht mehr verwendet werden, wie z. B. Dateien, Netzwerkverbindungen und Datenbankverbindungen. Verwenden Sie schwache Referenzen, um Speicherlecks zu verhindern, und zielen Sie auf Objekte für die Garbage Collection ab, wenn sie nicht mehr stark referenziert sind. Bei Verwendung von Go-Coroutine wird der Speicher des Coroutine-Stapels beim Beenden automatisch freigegeben, um Speicherverluste zu vermeiden.

Eine Anleitung zum Unit-Testen gleichzeitiger Go-Funktionen Eine Anleitung zum Unit-Testen gleichzeitiger Go-Funktionen May 03, 2024 am 10:54 AM

Das Testen gleichzeitiger Funktionen in Einheiten ist von entscheidender Bedeutung, da dies dazu beiträgt, ihr korrektes Verhalten in einer gleichzeitigen Umgebung sicherzustellen. Beim Testen gleichzeitiger Funktionen müssen grundlegende Prinzipien wie gegenseitiger Ausschluss, Synchronisation und Isolation berücksichtigt werden. Gleichzeitige Funktionen können Unit-Tests unterzogen werden, indem Rennbedingungen simuliert, getestet und Ergebnisse überprüft werden.

Wie verwende ich den Fehler-Wrapper von Golang? Wie verwende ich den Fehler-Wrapper von Golang? Jun 03, 2024 pm 04:08 PM

In Golang können Sie mit Fehler-Wrappern neue Fehler erstellen, indem Sie Kontextinformationen an den ursprünglichen Fehler anhängen. Dies kann verwendet werden, um die von verschiedenen Bibliotheken oder Komponenten ausgelösten Fehlertypen zu vereinheitlichen und so das Debuggen und die Fehlerbehandlung zu vereinfachen. Die Schritte lauten wie folgt: Verwenden Sie die Funktion „errors.Wrap“, um die ursprünglichen Fehler in neue Fehler umzuwandeln. Der neue Fehler enthält Kontextinformationen zum ursprünglichen Fehler. Verwenden Sie fmt.Printf, um umschlossene Fehler auszugeben und so mehr Kontext und Umsetzbarkeit bereitzustellen. Wenn Sie verschiedene Fehlertypen behandeln, verwenden Sie die Funktion „errors.Wrap“, um die Fehlertypen zu vereinheitlichen.

Wie erstelle ich eine priorisierte Goroutine in Go? Wie erstelle ich eine priorisierte Goroutine in Go? Jun 04, 2024 pm 12:41 PM

Es gibt zwei Schritte zum Erstellen einer Prioritäts-Goroutine in der Go-Sprache: Registrieren einer benutzerdefinierten Goroutine-Erstellungsfunktion (Schritt 1) ​​und Angeben eines Prioritätswerts (Schritt 2). Auf diese Weise können Sie Goroutinen mit unterschiedlichen Prioritäten erstellen, die Ressourcenzuteilung optimieren und die Ausführungseffizienz verbessern.

See all articles