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.
In unserem vorherigen Beitrag haben wir die Grundlagen von Goroutinen und Kanälen untersucht, den Bausteinen der Parallelität von Go. Lesen Sie hier:
Schauen wir uns nun an, wie diese Grundelemente zusammenwirken, um leistungsstarke Muster zu bilden, die reale Probleme lösen.
In diesem Beitrag werden wir Generatormuster behandeln und versuchen, sie zu visualisieren. Bereiten wir uns also vor, denn wir werden den gesamten Prozess begleiten.
Ein Generator ist wie eine Quelle, die kontinuierlich Werte produziert, die wir bei Bedarf konsumieren können.
In Go ist es eine Funktion, die einen Strom von Werten erzeugt und diese über einen Kanal sendet, sodass andere Teile unseres Programms diese Werte bei Bedarf empfangen können.
Sehen wir uns ein Beispiel an:
// generateNumbers creates a generator that produces numbers from 1 to max func generateNumbers(max int) chan int { // Create a channel to send numbers out := make(chan int) // Launch a goroutine to generate numbers go func() { // Important: Always close the channel when done defer close(out) for i := 1; i <= max; i++ { out <- i // Send number to channel } }() // Return channel immediately return out } // Using the generator func main() { // Create a generator that produces numbers 1-5 numbers := generateNumbers(5) // Receive values from the generator for num := range numbers { fmt.Println("Received:", num) } }
In diesem Beispiel erledigt unsere Generatorfunktion drei wichtige Dinge:
Große Dateien Zeile für Zeile lesen:
func generateLines(filename string) chan string { out := make(chan string) go func() { defer close(out) file, err := os.Open(filename) if err != nil { return } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { out <- scanner.Text() } }() return out }
Jetzt denken Sie vielleicht: Was ist das Besondere daran? Wir können das Gleiche tun wie das Generieren einer Datenfolge oder das zeilenweise Lesen ohne Goroutinen. Ist das nicht ein Overkill? Versuchen wir, beide Fälle zu visualisieren:
Ohne die Goroutinen
// Traditional approach func getNumbers(max int) []int { numbers := make([]int, max) for i := 1; i <= max; i++ { numbers[i-1] = i // Imagine some heavy computation here time.Sleep(100 * time.Millisecond) } return numbers }
Hier müssen Sie warten, bis alles fertig ist, bevor Sie mit der Verarbeitung beginnen können.
Mit Goroutinen
// Generator approach func generateNumbers(max int) chan int { out := make(chan int) go func() { defer close(out) for i := 1; i <= max; i++ { out <- i // Same heavy computation time.Sleep(100 * time.Millisecond) } }() return out }
Sie können mit der Verarbeitung der Daten beginnen, während die Daten noch generiert werden.
Nicht blockierende Ausführung: Generierung und Verarbeitung erfolgen gleichzeitig
Speichereffizienz: Kann jeweils einen Wert generieren und verarbeiten, ohne dass eine sofortige Speicherung im Speicher erforderlich ist
Unendliche Sequenzen: Kann unendliche Sequenzen ohne Speicherprobleme generieren
Umgang mit Gegendruck: Wenn Ihr Verbraucher langsam ist, verlangsamt sich der Generator auf natürliche Weise (aufgrund der Kanalblockierung), wodurch eine Speicherüberlastung vermieden wird.
// generateNumbers creates a generator that produces numbers from 1 to max func generateNumbers(max int) chan int { // Create a channel to send numbers out := make(chan int) // Launch a goroutine to generate numbers go func() { // Important: Always close the channel when done defer close(out) for i := 1; i <= max; i++ { out <- i // Send number to channel } }() // Return channel immediately return out } // Using the generator func main() { // Create a generator that produces numbers 1-5 numbers := generateNumbers(5) // Receive values from the generator for num := range numbers { fmt.Println("Received:", num) } }
func generateLines(filename string) chan string { out := make(chan string) go func() { defer close(out) file, err := os.Open(filename) if err != nil { return } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { out <- scanner.Text() } }() return out }
// Traditional approach func getNumbers(max int) []int { numbers := make([]int, max) for i := 1; i <= max; i++ { numbers[i-1] = i // Imagine some heavy computation here time.Sleep(100 * time.Millisecond) } return numbers }
// Generator approach func generateNumbers(max int) chan int { out := make(chan int) go func() { defer close(out) for i := 1; i <= max; i++ { out <- i // Same heavy computation time.Sleep(100 * time.Millisecond) } }() return out }
Das ist alles für das Generatormuster. Als nächstes folgt das Pipeline-Parallelitätsmuster. Bleiben Sie dran, um Ihre Konzepte zur Golang-Parallelität zu klären.
Habe ich etwas verpasst? Haben Sie Fragen? Haben Sie etwas Interessantes mitzuteilen? Alle Kommentare sind willkommen.
Das obige ist der detaillierte Inhalt vonGenerator-Parallelitätsmuster in Go: Ein umfassender Leitfaden. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!