1. Führen Sie jedes Beispiel aus: Lesen Sie nicht nur den Code. Geben Sie es ein, führen Sie es aus und beobachten Sie das Verhalten.⚠️ Wie geht man bei dieser Serie vor?
2. Experimentieren Sie und machen Sie Dinge kaputt: Entfernen Sie Schlafphasen und sehen Sie, was passiert, ändern Sie die Kanalpuffergrößen, ändern Sie die Anzahl der Goroutinen.
Wenn man Dinge kaputt macht, lernt man, wie sie funktionieren
3. Grund für das Verhalten: Bevor Sie geänderten Code ausführen, versuchen Sie, das Ergebnis vorherzusagen. Wenn Sie unerwartetes Verhalten bemerken, halten Sie inne und überlegen Sie, warum. Stellen Sie die Erklärungen in Frage.
4. Erstellen Sie mentale Modelle: Jede Visualisierung repräsentiert ein Konzept. Versuchen Sie, Ihre eigenen Diagramme für geänderten Code zu zeichnen.
Dies ist Teil 1 unserer Serie „Mastering Go Concurrency“, in der wir Folgendes behandeln:
- Wie Goroutinen funktionieren und ihr Lebenszyklus
- Kanalkommunikation zwischen Goroutinen
- Gepufferte Kanäle und ihre Anwendungsfälle
- Praxisbeispiele und Visualisierungen
Wir beginnen mit den Grundlagen und entwickeln schrittweise ein Gespür dafür, wie wir diese effektiv nutzen können.
Es wird etwas lang, eher sehr lang, also macht euch bereit.
Wir stehen Ihnen während des gesamten Prozesses zur Seite.
Grundlagen von Goroutinen
Beginnen wir mit einem einfachen Programm, das mehrere Dateien herunterlädt.
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") startTime := time.Now() downloadFile("file1.txt") downloadFile("file2.txt") downloadFile("file3.txt") elapsedTime := time.Since(startTime) fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime) }
Das Programm dauert insgesamt 6 Sekunden, da jeder 2-sekündige Download abgeschlossen sein muss, bevor der nächste beginnt. Stellen wir uns das vor:
Wir können diese Zeit verkürzen, indem wir unser Programm so ändern, dass es Go-Routinen verwendet:
Hinweis: Schlüsselwort vor Funktionsaufruf eingeben
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") // Launch downloads concurrently go downloadFile("file1.txt") go downloadFile("file2.txt") go downloadFile("file3.txt") fmt.Println("All downloads completed!") }
Warte was? wurde nichts gedruckt? Warum?
Lassen Sie uns dies visualisieren, um zu verstehen, was passieren könnte.
Aus der obigen Visualisierung gehen wir hervor, dass die Hauptfunktion existiert, bevor die Goroutinen fertig sind. Eine Beobachtung ist, dass der gesamte Lebenszyklus einer Goroutine von der Hauptfunktion abhängt.
Hinweis: Die Hauptfunktion an sich ist eine Goroutine ;)
Um dies zu beheben, müssen wir die Haupt-Goroutine warten lassen, bis die anderen Goroutinen abgeschlossen sind. Dafür gibt es mehrere Möglichkeiten:
- Warten Sie ein paar Sekunden (knifflige Art)
- WaitGroup verwenden (richtige Vorgehensweise, als nächstes)
- Verwendung von Kanälen (wir werden weiter unten darauf eingehen)
Lassen Sie uns einige Sekunden warten, bis die Go-Routinen abgeschlossen sind.
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") startTime := time.Now() downloadFile("file1.txt") downloadFile("file2.txt") downloadFile("file3.txt") elapsedTime := time.Since(startTime) fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime) }
Das Problem dabei ist, dass wir möglicherweise nicht wissen, wie viel Zeit eine Goroutine dauern könnte. In diesem Fall haben wir jeweils eine konstante Zeit, aber in realen Szenarien sind wir uns bewusst, dass die Downloadzeit variiert.
Kommt die sync.WaitGroup
Eine sync.WaitGroup in Go ist ein Parallelitätskontrollmechanismus, der verwendet wird, um darauf zu warten, dass die Ausführung einer Sammlung von Goroutinen abgeschlossen ist.
Hier sehen wir uns das in Aktion an und visualisieren es:
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") // Launch downloads concurrently go downloadFile("file1.txt") go downloadFile("file2.txt") go downloadFile("file3.txt") fmt.Println("All downloads completed!") }
Lassen Sie uns dies visualisieren und die Funktionsweise von sync.WaitGroup verstehen:
Gegenmechanismus:
- WaitGroup unterhält einen internen Zähler
- wg.Add(n) erhöht den Zähler um n
- wg.Done() dekrementiert den Zähler um 1
- wg.Wait() blockiert, bis der Zähler 0 erreicht
Synchronisierungsablauf:
- Haupt-Goroutine ruft Add(3) auf, bevor Goroutinen gestartet werden
- Jede Goroutine ruft Done() auf, wenn sie abgeschlossen ist
- Die Hauptgoroutine wird bei Wait() blockiert, bis der Zähler 0 erreicht
- Wenn der Zähler 0 erreicht, wird das Programm fortgesetzt und sauber beendet
Häufige Fallstricke, die es zu vermeiden gilt
package main
import (
"fmt"
"time"
)
func downloadFile(filename string) {
fmt.Printf("Starting download: %s\n", filename)
// Simulate file download with sleep
time.Sleep(2 * time.Second)
fmt.Printf("Finished download: %s\n", filename)
}
func main() {
fmt.Println("Starting downloads...")
startTime := time.Now() // Record start time
go downloadFile("file1.txt")
go downloadFile("file2.txt")
go downloadFile("file3.txt")
// Wait for goroutines to finish
time.Sleep(3 * time.Second)
elapsedTime := time.Since(startTime)
fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime)
}
Kanäle
So haben wir ein gutes Verständnis dafür bekommen, wie die Goroutinen funktionieren. Nein, wie kommunizieren zwei Go-Routinen? Hier kommt der Kanal ins Spiel.
Kanäle in Go sind ein leistungsstarkes Nebenläufigkeitsprimitiv, das für die Kommunikation zwischen Goroutinen verwendet wird. Sie bieten Goroutinen die Möglichkeit, Daten sicher auszutauschen.
Stellen Sie sich Kanäle als Pipes vor: Eine Goroutine kann Daten an einen Kanal senden und eine andere kann sie empfangen.
hier sind einige Eigenschaften:
- Kanäle blockieren von Natur aus.
- Eine An Kanal senden Operation ch blockiert, bis eine andere Goroutine vom Kanal empfängt.
- Eine Vom Kanal empfangen-Operation blockiert, bis eine andere Goroutine an den Kanal sendet.
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") startTime := time.Now() downloadFile("file1.txt") downloadFile("file2.txt") downloadFile("file3.txt") elapsedTime := time.Since(startTime) fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime) }
Warum führt ch
Beheben wir dieses Problem, indem wir eine Goroutine hinzufügen
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") // Launch downloads concurrently go downloadFile("file1.txt") go downloadFile("file2.txt") go downloadFile("file3.txt") fmt.Println("All downloads completed!") }
Lass uns das visualisieren:
Diesmal wird die Nachricht von einer anderen Goroutine gesendet, damit die Haupt-Goroutine nicht blockiert wird, während sie an den Kanal gesendet wird, sodass sie zu msg :=
Behebung des Hauptproblems, bei der Verwendung des Kanals nicht auf andere zu warten
Jetzt verwenden wir den Kanal, um das Problem mit dem Datei-Downloader zu beheben (main wartet nicht, bis andere fertig sind).
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") startTime := time.Now() // Record start time go downloadFile("file1.txt") go downloadFile("file2.txt") go downloadFile("file3.txt") // Wait for goroutines to finish time.Sleep(3 * time.Second) elapsedTime := time.Since(startTime) fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime) }
Visualisierung:
Machen wir einen Probelauf, um es besser zu verstehen:
Programmstart:
Hauptgoroutine erstellt Fertigkanal
Startet drei Download-Goroutinen
Jede Goroutine erhält einen Verweis auf denselben Kanal
Ausführung herunterladen:
- Alle drei Downloads laufen gleichzeitig
- Jeder dauert 2 Sekunden
- Sie können in beliebiger Reihenfolge enden
Kanalschleife:
- Hauptgoroutine tritt in die Schleife ein: for i := 0; ich
- Jedes
- Die Schleife stellt sicher, dass wir auf alle drei Abschlusssignale warten
Schleifenverhalten:
- Iteration 1: Blockiert, bis der erste Download abgeschlossen ist
- Iteration 2: Blockiert, bis der zweite Download abgeschlossen ist
- Iteration 3: Blockiert, bis der endgültige Download abgeschlossen ist
Die Reihenfolge der Fertigstellung spielt keine Rolle!
Beobachtungen:
⭐ Jeder Versand (erledigt ⭐ Die Hauptgoroutine koordiniert alles durch die Schleife
Wie können zwei Goroutinen kommunizieren?
Wir haben bereits gesehen, wie zwei Goroutinen kommunizieren können. Wann? Die ganze Zeit. Vergessen wir nicht, dass die Hauptfunktion auch eine Goroutine ist.
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") startTime := time.Now() downloadFile("file1.txt") downloadFile("file2.txt") downloadFile("file3.txt") elapsedTime := time.Since(startTime) fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime) }
Lassen Sie uns dies visualisieren und einen Probelauf durchführen:
Probelauf:
Programmstart (t=0ms)
Erste Nachricht (t=1ms)
Zweite Nachricht (t=101ms)
Dritte Nachricht (t=201ms)
Kanal schließen (t=301 ms)
Abschluss (t=302–303 ms)
Gepufferte Kanäle
Warum brauchen wir gepufferte Kanäle?
Ungepufferte Kanäle blockieren sowohl den Sender als auch den Empfänger, bis die andere Seite bereit ist. Wenn Hochfrequenzkommunikation erforderlich ist, können ungepufferte Kanäle zu einem Engpass werden, da beide Goroutinen pausieren müssen, um Daten auszutauschen.
Eigenschaften gepufferter Kanäle:
- FIFO (First In, First Out, ähnlich der Warteschlange)
- Feste Größe, bei der Erstellung festgelegt
- Blockiert den Absender, wenn der Puffer voll ist
- Blockiert den Empfänger, wenn der Puffer leer ist
Wir sehen es in Aktion:
package main import ( "fmt" "time" ) func downloadFile(filename string) { fmt.Printf("Starting download: %s\n", filename) // Simulate file download with sleep time.Sleep(2 * time.Second) fmt.Printf("Finished download: %s\n", filename) } func main() { fmt.Println("Starting downloads...") startTime := time.Now() downloadFile("file1.txt") downloadFile("file2.txt") downloadFile("file3.txt") elapsedTime := time.Since(startTime) fmt.Printf("All downloads completed! Time elapsed: %s\n", elapsedTime) }
Ausgabe (vor dem Auskommentieren des ch
Warum hat es die Haupt-Goroutine nicht blockiert?
Ein gepufferter Kanal ermöglicht das Senden bis zu seiner Kapazität, ohneden Absender zu blockieren.
Der Kanal hat eine Kapazität von 2, was bedeutet, dass er zwei Werte in seinem Puffer speichern kann, bevor er blockiert wird.
Der Puffer ist bereits mit „erster“ und „zweiter“ voll. Da es keinen gleichzeitigen Empfänger gibt, der diese Werte konsumiert, wird der Sendevorgang auf unbestimmte Zeit blockiert.
Da die Haupt-Goroutine auch für das Senden verantwortlich ist und es keine anderen aktiven Goroutinen gibt, die Werte vom Kanal empfangen, gerät das Programm beim Versuch, die dritte Nachricht zu senden, in einen Deadlock.
Das Auskommentieren der dritten Nachricht führt zu einem Deadlock, da die Kapazität jetzt voll ist und die dritte Nachricht blockiert wird, bis der Puffer frei wird.
Wann sollten gepufferte Kanäle im Vergleich zu ungepufferten Kanälen verwendet werden?
Aspect | Buffered Channels | Unbuffered Channels |
---|---|---|
Purpose | For decoupling sender and receiver timing. | For immediate synchronization between sender and receiver. |
When to Use | - When the sender can proceed without waiting for receiver. | - When sender and receiver must synchronize directly. |
- When buffering improves performance or throughput. | - When you want to enforce message-handling immediately. | |
Blocking Behavior | Blocks only when buffer is full. | Sender blocks until receiver is ready, and vice versa. |
Performance | Can improve performance by reducing synchronization. | May introduce latency due to synchronization. |
Example Use Cases | - Logging with rate-limited processing. | - Simple signaling between goroutines. |
- Batch processing where messages are queued temporarily. | - Hand-off of data without delay or buffering. | |
Complexity | Requires careful buffer size tuning to avoid overflows. | Simpler to use; no tuning needed. |
Overhead | Higher memory usage due to the buffer. | Lower memory usage; no buffer involved. |
Concurrency Pattern | Asynchronous communication between sender and receiver. | Synchronous communication; tight coupling. |
Error-Prone Scenarios | Deadlocks if buffer size is mismanaged. | Deadlocks if no goroutine is ready to receive or send. |
Wichtige Erkenntnisse
Verwenden Sie gepufferte Kanäle, wenn:
- Sie müssen das Timing von Sender und Empfänger entkoppeln.
- Die Leistung kann durch das Stapeln oder Einreihen von Nachrichten in die Warteschlange verbessert werden.
- Die Anwendung kann Verzögerungen bei der Verarbeitung von Nachrichten tolerieren, wenn der Puffer voll ist.
Verwenden Sie ungepufferte Kanäle, wenn:
- Die Synchronisierung zwischen Goroutinen ist von entscheidender Bedeutung.
- Sie möchten Einfachheit und eine sofortige Übergabe von Daten.
- Die Interaktion zwischen Sender und Empfänger muss sofort erfolgen.
Diese Grundlagen schaffen die Grundlage für fortgeschrittenere Konzepte. In unseren kommenden Beiträgen werden wir Folgendes untersuchen:
Nächster Beitrag:
- Parallelitätsmuster
- Mutex und Speichersynchronisation
Bleiben Sie dran, während wir unser Verständnis der leistungsstarken Parallelitätsfunktionen von Go weiter ausbauen!
Das obige ist der detaillierte Inhalt vonGoroutinen und Kanäle in Golang mit intuitiven Bildern verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Golang eignet sich besser für hohe Parallelitätsaufgaben, während Python mehr Vorteile bei der Flexibilität hat. 1. Golang behandelt die Parallelität effizient über Goroutine und Kanal. 2. Python stützt sich auf Threading und Asyncio, das von GIL betroffen ist, jedoch mehrere Parallelitätsmethoden liefert. Die Wahl sollte auf bestimmten Bedürfnissen beruhen.

Die Leistungsunterschiede zwischen Golang und C spiegeln sich hauptsächlich in der Speicherverwaltung, der Kompilierungsoptimierung und der Laufzeiteffizienz wider. 1) Golangs Müllsammlung Mechanismus ist praktisch, kann jedoch die Leistung beeinflussen.

Wählen SieGolangforHighperformanceConcurcurrency, idealforbackendServicesandNetworkProgramming; selectPythonforrapidDevelopment, DataScience und MachinelearningDuEToSverseStilityAntenSiveselibrary.

Golang und Python haben jeweils ihre eigenen Vorteile: Golang ist für hohe Leistung und gleichzeitige Programmierung geeignet, während Python für Datenwissenschaft und Webentwicklung geeignet ist. Golang ist bekannt für sein Parallelitätsmodell und seine effiziente Leistung, während Python für sein Ökosystem für die kurze Syntax und sein reiches Bibliothek bekannt ist.

In welchen Aspekten sind Golang und Python einfacher zu verwenden und haben eine glattere Lernkurve? Golang eignet sich besser für hohe Parallelitäts- und hohe Leistungsbedürfnisse, und die Lernkurve ist für Entwickler mit C -Sprachhintergrund relativ sanft. Python eignet sich besser für Datenwissenschaft und schnelles Prototyping, und die Lernkurve ist für Anfänger sehr reibungslos.

Golang und C haben jeweils ihre eigenen Vorteile bei Leistungswettbewerben: 1) Golang ist für eine hohe Parallelität und schnelle Entwicklung geeignet, und 2) C bietet eine höhere Leistung und eine feinkörnige Kontrolle. Die Auswahl sollte auf Projektanforderungen und Teamtechnologie -Stack basieren.

Golang eignet sich für eine schnelle Entwicklung und gleichzeitige Programmierung, während C für Projekte, die eine extreme Leistung und die zugrunde liegende Kontrolle erfordern, besser geeignet sind. 1) Golangs Parallelitätsmodell vereinfacht die Parallelitätsprogrammierung durch Goroutine und Kanal. 2) Die Vorlagenprogrammierung von C bietet generische Code und Leistungsoptimierung. 3) Golangs Müllsammlung ist bequem, kann jedoch die Leistung beeinflussen. Die Speicherverwaltung von C ist komplex, aber die Kontrolle ist in Ordnung.

GoimpactsDevelopmentPositivyThroughSpeed, Effizienz und DiasMlitication.1) Geschwindigkeit: Gocompilesquickandrunseffiction, idealforlargeProjects


Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

AI Hentai Generator
Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

SublimeText3 Linux neue Version
SublimeText3 Linux neueste Version

Herunterladen der Mac-Version des Atom-Editors
Der beliebteste Open-Source-Editor

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

VSCode Windows 64-Bit-Download
Ein kostenloser und leistungsstarker IDE-Editor von Microsoft