Heim > Artikel > Backend-Entwicklung > Beherrschen Sie den Parallelitätsmodus und das parallele Rechnen der Go-Sprache
Mit der rasanten Entwicklung des Internets wird die Nachfrage nach großen verteilten Systemen immer größer, und gleichzeitiges Programmieren und paralleles Rechnen sind zu Fähigkeiten geworden, die Internetentwickler beherrschen müssen. Die Go-Sprache ist eine Sprache, die zur Unterstützung der Parallelität entwickelt wurde. Sie eignet sich sehr gut für gleichzeitige Programmierung und paralleles Rechnen. In diesem Artikel werden der Parallelitätsmodus und das parallele Rechnen der Go-Sprache vorgestellt und einige praktische Fälle aufgeführt, um den Lesern ein tieferes Verständnis zu ermöglichen.
1. Parallelitätsmodus der Go-Sprache
Der Parallelitätsmodus der Go-Sprache basiert hauptsächlich auf den beiden Grundkomponenten Goroutine und Kanal. Goroutine ist ein leichter Thread, der vom Laufzeitsystem der Go-Sprache verwaltet wird. Er kann über das Schlüsselwort go gestartet werden und Goroutinen können über Kanäle kommunizieren.
Das Folgende ist ein einfaches Beispiel für Goroutine und Kanal:
package main import "fmt" func printMsg(msg string, ch chan string) { ch <- msg } func main() { ch := make(chan string) msgs := []string{"Hello", "Golang", "Parallel"} for _, msg := range msgs { go printMsg(msg, ch) } for i := 0; i < len(msgs); i++ { fmt.Println(<-ch) } }
Der Code startet drei Goroutinen über eine for-Schleife und gibt jeweils drei Zeichenfolgen aus. Die printMsg-Funktion schreibt die Zeichenfolgennachricht in den Kanal und die Hauptfunktion liest erneut aus dem Kanal.
1.1 Pipeline-Modus
In der Go-Sprache können mehrere Goroutinen über den Pipeline-Modus in Reihe geschaltet werden, um ein komplexeres gleichzeitiges System zu bilden. Die Implementierung des Pipeline-Musters erfolgt normalerweise über die Kanalkommunikation zwischen mehreren Goroutinen, die Weitergabe von Daten von einer Goroutine an eine andere Goroutine und die Verarbeitung und Konvertierung der Daten in jeder Goroutine. Das Folgende ist ein einfaches Beispiel für den Pipeline-Modus:
package main import ( "fmt" ) func addOne(in <-chan int, out chan<- int) { for val := range in { out <- val + 1 } close(out) } func printNums(out <-chan int) { for val := range out { fmt.Println(val) } } func main() { nums := []int{1, 2, 3} in := make(chan int) out := make(chan int) go addOne(in, out) go printNums(out) for _, num := range nums { in <- num } close(in) }
Der Code definiert 3 Goroutinen, nämlich Eingabe-Goroutine, plus 1 Verarbeitungs-Goroutine und Ausgabe-Goroutine. Die Funktion addOne fügt 1 zu den Daten im Eingabekanal hinzu und schreibt sie in den Ausgabekanal. printNums Die Funktion liest Daten aus dem Ausgabekanal und gibt sie aus.
1.2 Auswahlmuster
Die Select-Anweisung der Go-Sprache bietet eine bequeme Möglichkeit, mehrere Kanäle zu verarbeiten, nämlich das Auswahlmuster (Auswahlmuster). Der Auswahlmodus ermöglicht es uns, nicht blockierende Auswahlvorgänge in mehreren Kanälen durchzuführen. Wenn in mehreren Kanälen lesbare oder beschreibbare Nachrichten vorhanden sind, wird automatisch einer für den Vorgang ausgewählt.
Das Folgende ist ein einfaches Beispiel für einen Auswahlmodus:
package main import "fmt" func ping(ch chan<- string) { for { ch <- "ping" } } func pong(ch chan<- string) { for { ch <- "pong" } } func printer(ch <-chan string) { for { fmt.Println(<-ch) } } func main() { ch1 := make(chan string) ch2 := make(chan string) ch3 := make(chan string) go ping(ch1) go pong(ch2) go printer(ch3) for { select { case msg := <-ch1: ch3 <- msg case msg := <-ch2: ch3 <- msg } } }
Im Code senden die Ping-Funktion und die Pong-Funktion „Ping“- und „Pong“-Nachrichten an Kanal 1 bzw. Kanal 2, und die Druckerfunktion liest die Nachricht in Kanal 3 und gibt sie aus Es. Verwenden Sie in der Hauptfunktion die Select-Anweisung, um die Nachrichten in Kanal 1 und Kanal 2 zu überwachen und die empfangenen Nachrichten über Kanal 3 zur Ausgabe an die Druckerfunktion weiterzuleiten.
2. Parallele Berechnung der Go-Sprache
Die integrierten parallelen Berechnungsmodule der Go-Sprache umfassen Synchronisierung, Atomic und Kontext usw. sync und atomic verwenden hauptsächlich Mutex (Mutex) und atomare Operationen (atomare Operationen), um den gleichzeitigen Datenzugriff zu steuern, und Kontext wird zum Verwalten der Kontextinformationen von Goroutine verwendet. Hier finden Sie eine kurze Einführung in die Verwendung dieser Module:
2.1 Mutex-Sperre
Mutex-Sperre ist einer der am häufigsten verwendeten Synchronisationsmechanismen zum Schutz gemeinsam genutzter Ressourcen und außerdem einer der grundlegendsten Synchronisationsmechanismen in der Go-Sprache . In der Go-Sprache können Sie über den Mutex-Typ im Synchronisierungspaket eine Mutex-Sperre erstellen. Der Mutex-Typ bietet zwei wichtige Methoden: Sperren und Entsperren. Bevor Sie auf gemeinsam genutzte Ressourcen zugreifen, müssen Sie die Lock-Methode aufrufen, um die Sperre zu erhalten, und dann die Unlock-Methode aufrufen, um die Sperre aufzuheben, nachdem der Zugriff abgeschlossen ist. Das Folgende ist ein einfaches Beispiel für eine Mutex-Sperre:
package main import ( "fmt" "sync" ) func addOne(num *int, mutex *sync.Mutex, wg *sync.WaitGroup) { mutex.Lock() *num += 1 mutex.Unlock() wg.Done() } func main() { var wg sync.WaitGroup var num int mutex := &sync.Mutex{} for i := 0; i < 1000; i++ { wg.Add(1) go addOne(&num, mutex, &wg) } wg.Wait() fmt.Println(num) }
Im Code ist die Funktion addOne so definiert, dass sie 1 zur Variablen num hinzufügt. Die Mutex-Sperre muss vor dem Hinzufügen von 1 erhalten werden, und die Mutex-Sperre muss nach dem Hinzufügen aufgehoben werden 1. Verwenden Sie WaitGroup, um zu warten, bis alle Goroutinen die Ausführung abgeschlossen haben, und geben Sie die Endergebnisse aus.
2.2 Atomare Operationen
In Szenarien mit hoher Parallelität können Mutex-Sperren die Programmleistung beeinträchtigen, daher bietet die Go-Sprache atomare Operationen als Ersatz für Mutex-Sperren. Das Atompaket bietet mehrere atomare Operationsfunktionen wie AddInt64, CompareAndSwapInt64, SwapInt64 usw. Durch die Verwendung atomarer Operationen wird sichergestellt, dass Operationen an Variablen nicht durch andere Goroutinen unterbrochen werden und die gleichzeitige Ausführung nicht beeinträchtigt wird. Das Folgende ist ein einfaches Beispiel für eine atomare Operation:
package main import ( "fmt" "sync/atomic" ) func addOne(num *int64, count *int64, done chan bool) { for i := 0; i < 1000; i++ { atomic.AddInt64(num, 1) } atomic.AddInt64(count, 1) done <- true } func main() { var num int64 var count int64 done := make(chan bool) for i := 0; i < 100; i++ { go addOne(&num, &count, done) } for i := 0; i < 100; i++ { <-done } fmt.Printf("num=%d, count=%d ", num, count) }
Im Code wird die AddInt64-Funktion des Atompakets verwendet, um atomare Operationen an der Num-Variablen auszuführen. Nach Abschluss der Operation wird der Hauptthread durch erledigt benachrichtigt. Die Zählvariable wird über die normale AddInt64-Funktion akkumuliert und die Werte von num und count werden schließlich ausgegeben.
2.3 Kontextverwaltung
In der Go-Sprache ist es häufig erforderlich, Kontextinformationen zwischen mehreren Goroutinen zu übergeben, wie z. B. Anforderungs-ID, Timeout-Einstellungen usw. Das Kontextpaket bietet eine praktische Möglichkeit, Goroutine-Kontextinformationen zu verwalten. Wenn Sie Kontext verwenden, müssen Sie normalerweise einen übergeordneten Kontext in der Haupt-Goroutine erstellen. Verwenden Sie beim Ableiten der Goroutine WithCancel, WithDeadline, WithValue und andere Funktionen, um einen untergeordneten Kontext zu erstellen und die entsprechenden Kontextinformationen zu übergeben. Das Folgende ist ein einfaches Beispiel für die Kontextverwaltung:
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("worker %d canceled ", id) return default: fmt.Printf("worker %d is working ", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) for i := 0; i < 3; i++ { go worker(ctx, i) } time.Sleep(5 * time.Second) cancel() }
Verwenden Sie im Code das Kontextpaket, um einen übergeordneten Kontext zu erstellen, und erstellen Sie über die WithCancel-Funktion einen untergeordneten Kontext. Verwenden Sie in der Worker-Funktion die SELECT-Anweisung, um das Signal von ctx.Done() abzuhören. Wenn ctx.Done() geschlossen ist, bedeutet dies, dass der Kontext abgebrochen wird und die Worker-Funktion beendet werden muss. Schließen Sie in der Hauptfunktion den Unterkontext über die Abbruchfunktion und warten Sie, bis der Unterkontext abgebrochen wird. Die laufenden Ergebnisse lauten wie folgt:
worker 0 is working worker 1 is working worker 2 is working worker 2 canceled worker 1 canceled worker 0 canceled
Wenn der übergeordnete Kontext abgebrochen wird, erhalten alle untergeordneten Kontexte Benachrichtigungen und beenden die Ausführung.
3. Fazit
Dieser Artikel stellt kurz den Parallelitätsmodus und das parallele Rechnen der Go-Sprache vor und stellt grundlegende Komponenten und Module wie Goroutine, Kanal, Mutex-Sperre, atomare Operation und Kontext vor. Durch das Erlernen dieser Grundkenntnisse können wir die Parallelität und parallele Programmierung der Go-Sprache besser beherrschen und den Grundstein für die Erstellung leistungsstarker Internetanwendungen mit hoher Parallelität legen.
Das obige ist der detaillierte Inhalt vonBeherrschen Sie den Parallelitätsmodus und das parallele Rechnen der Go-Sprache. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!