Heim > Artikel > Backend-Entwicklung > Detaillierte grafische Erläuterung des Parallelitätsmechanismus der Go-Sprache
Go-SpracheEiner der großen Vorteile gegenüber Java und anderen besteht darin, dass es problemlos gleichzeitige Programme schreiben kann. Die Go-Sprache verfügt über einen integrierten Goroutine-Mechanismus. Mit Goroutine können Sie schnell gleichzeitige Programme entwickeln und Multi-Core-Prozessorressourcen besser nutzen. In diesem Artikel erfahren Sie mehr über die Anwendung von Goroutine und seine Planungsimplementierung.
1. Unterstützung der Go-Sprache für Parallelität
Verwenden Sie Goroutine-Programmierung (empfohlen: go Video-Tutorial )
Verwenden Sie das Schlüsselwort „go“, um eine Goroutine zu erstellen. Platzieren Sie die Go-Deklaration vor einer Funktion, die aufgerufen werden muss, und rufen Sie die Funktion auf und führen Sie sie im selben Adressraum aus, sodass die Funktion als unabhängiger gleichzeitiger Thread ausgeführt wird. Diese Art von Thread wird in der Go-Sprache Goroutine genannt.
Die Verwendung von Goroutine ist wie folgt:
//go 关键字放在方法调用前新建一个 goroutine 并执行方法体 go GetThingDone(param1, param2); //新建一个匿名方法并执行 go func(param1, param2) { }(val1, val2) //直接新建一个 goroutine 并在 goroutine 中执行代码块 go { //do someting... }
Weil Goroutine in einer Multi-Core-CPU-Umgebung parallel ist. Wenn ein Codeblock in mehreren Goroutinen ausgeführt wird, erreichen wir Codeparallelität.
Wenn Sie die Ausführung des Programms wissen müssen, wie erhalten Sie dann die parallelen Ergebnisse? Es muss in Verbindung mit dem Kanal verwendet werden.
Verwenden Sie Kanäle, um die Parallelität zu steuern
Kanäle werden verwendet, um gleichzeitig ausgeführte Funktionen zu synchronisieren und ihnen eine Art wertübergebenden Kommunikationsmechanismus bereitzustellen.
Der Elementtyp, der Container (oder Puffer) und die Übertragungsrichtung, die durch den Kanal geleitet werden, werden durch den Operator „
Sie können die integrierte Funktion make verwenden, um einen Kanal zuzuweisen:
i := make(chan int) // by default the capacity is 0 s := make(chan string, 3) // non-zero capacity r := make(<-chan bool) // can only read from w := make(chan<- []os.FileInfo) // can only write to
runtime.GOMAXPROCS konfigurieren
Verwenden Sie den folgenden Code, um ihn explizit festzulegen ob Multi-Core verwendet werden soll, um gleichzeitige Aufgaben auszuführen:
runtime.GOMAXPROCS()
Die Anzahl der GOMAXPROCS kann entsprechend der Anzahl der Aufgaben zugewiesen werden, sie sollte jedoch nicht größer sein als die Anzahl der CPU-Kerne.
Die Konfiguration der parallelen Ausführung eignet sich besser für CPU-intensive und hochparallele Szenarien. Wenn sie IO-intensiv ist, erhöht die Verwendung mehrerer Kerne den durch CPU-Umschaltung verursachten Leistungsverlust.
Nachdem wir den Parallelitätsmechanismus der Go-Sprache verstanden haben, werfen wir einen Blick auf die spezifische Implementierung des Goroutine-Mechanismus.
2. Der Unterschied zwischen Parallelität und Parallelität
Prozess, Thread und Prozessor
In modernen Betriebssystemen sind Threads die Grundeinheit der Prozessorplanung und -zuweisung, und Prozesse sind die Grundeinheit des Ressourcenbesitzes. Jeder Prozess besteht aus einem privaten virtuellen Adressraum, Code, Daten und anderen verschiedenen Systemressourcen. Ein Thread ist eine Ausführungseinheit innerhalb eines Prozesses. Jeder Prozess verfügt über mindestens einen Hauptausführungsthread, der nicht aktiv vom Benutzer erstellt werden muss, sondern automatisch vom System erstellt wird. Benutzer erstellen nach Bedarf weitere Threads in der Anwendung, und mehrere Threads werden gleichzeitig im selben Prozess ausgeführt.
Parallelität und Parallelität
Parallelität und Parallelität (Parallelität und Parallelität) sind zwei verschiedene Konzepte, um das Multithreading-Modell zu verstehen.
Bei der Beschreibung der Parallelität oder Parallelität eines Programms sollte diese aus der Perspektive eines Prozesses oder Threads angegeben werden.
Gleichzeitigkeit: Es gibt viele Threads oder Prozesse, die in einem Zeitraum ausgeführt werden, aber zu jedem Zeitpunkt wird nur einer ausgeführt. Mehrere Threads oder Prozesse konkurrieren um Zeitscheiben und führen abwechselnd
Parallel aus : Mehrere Threads oder Prozesse werden in einem Zeitraum und zu einem bestimmten Zeitpunkt ausgeführt.
Ein nicht gleichzeitiges Programm verfügt nur über eine vertikale Steuerlogik. Das Programm befindet sich zu jedem Zeitpunkt nur in einer bestimmten Position dieser Steuerlogik . Das heißt, sequentielle Ausführung. Wenn ein Programm zu einem bestimmten Zeitpunkt von mehreren CPU-Pipelines gleichzeitig verarbeitet wird, spricht man davon, dass das Programm parallel läuft.
Parallelität erfordert Hardwareunterstützung. Single-Core-Prozessoren können nur gleichzeitig arbeiten, während Multi-Core-Prozessoren eine parallele Ausführung erreichen können.
Parallelität ist eine notwendige Bedingung für Parallelität. Wenn ein Programm selbst nicht gleichzeitig ist, das heißt, es gibt nur eine logische Ausführungssequenz, können wir nicht zulassen, dass es parallel verarbeitet wird.
Parallelität ist keine ausreichende Bedingung für Parallelität. Wenn ein gleichzeitiges Programm nur von einer CPU verarbeitet wird (durch Zeitteilung), dann ist es nicht parallel.
Schreiben Sie beispielsweise das einfachste sequentielle Strukturprogramm zur Ausgabe von „Hello World“, das nicht gleichzeitig ist. Wenn Sie dem Programm mehrere Threads hinzufügen und jeder Thread ein „Hello World“ ausgibt, dann ist dies der Fall gleichzeitig. Wenn diesem Programm zur Laufzeit nur eine einzige CPU zugewiesen wird, ist dieses gleichzeitige Programm noch nicht parallel und muss auf einem Mehrkernprozessor bereitgestellt werden, um Programmparallelität zu erreichen.
3. Mehrere verschiedene Multithreading-Modelle
Benutzer-Threads und Kernel-Level-Threads
Die Implementierung von Threads kann in zwei Kategorien unterteilt werden: Threads auf Benutzerebene (User-LevelThread, ULT) und Threads auf Kernelebene (Kemel-LevelThread, KLT). Benutzer-Threads werden durch Benutzercode unterstützt, und Kernel-Threads werden durch den Betriebssystemkernel unterstützt.
Multithreading-Modell
Das Multithreading-Modell umfasst die verschiedenen Verbindungsmethoden von Threads auf Benutzerebene und Threads auf Kernelebene.
(1) Viele-zu-eins-Modell (M: 1)
Ordnen Sie mehrere Threads auf Benutzerebene einem Thread auf Kernelebene zu, und die Thread-Verwaltung wird im Benutzerbereich abgeschlossen. In diesem Modus sind Threads auf Benutzerebene für das Betriebssystem unsichtbar (d. h. transparent).
Vorteile: Der Vorteil dieses Modells besteht darin, dass der Thread-Kontextwechsel im Benutzerbereich erfolgt, wodurch ein Moduswechsel vermieden wird, was sich positiv auf die Leistung auswirkt.
Nachteile: Alle Threads basieren auf einer Kernel-Planungseinheit, nämlich dem Kernel-Thread, was bedeutet, dass nur ein Prozessor verwendet werden kann. Dies ist im Wesentlichen nicht akzeptabel löst nur das Parallelitätsproblem, nicht jedoch das Parallelitätsproblem. Wenn ein Thread aufgrund einer E/A-Operation in den Kernel-Status fällt und der Kernel-Status-Thread das Warten auf E/A-Daten blockiert, werden alle Threads blockiert. Der Benutzerraum kann auch nicht blockierende E/A verwenden, jedoch Leistung und Komplexität lässt sich nicht vermeiden.
(2) Eins-zu-eins-Modell (1:1)
Ordnet jeden Thread auf Benutzerebene einem Thread auf Kernelebene zu.
Jeder Thread wird vom Kernel-Scheduler unabhängig geplant. Wenn also ein Thread blockiert, hat dies keine Auswirkungen auf andere Threads.
Vorteile: Durch die Unterstützung von Multi-Core-Prozessorhardware unterstützt das Kernel-Space-Thread-Modell echte Parallelität. Wenn ein Thread blockiert ist, kann ein anderer Thread weiter ausgeführt werden, sodass die Parallelitätsfähigkeit stark ist.
Nachteile: Jedes Mal, wenn ein Thread auf Benutzerebene erstellt wird, muss ein entsprechender Thread auf Kernelebene erstellt werden. Dadurch wird ein Thread erstellt, der relativ teuer ist und die Leistung der Anwendung beeinträchtigt.
(3) Viele-zu-viele-Modell (M : N)
Das Zahlenverhältnis von Kernel-Threads und Benutzer-Threads beträgt M : N. Der Kernel-Benutzerraum kombiniert die Vorteile des ersten zwei.
Dieses Modell erfordert die Zusammenarbeit des Kernel-Thread-Schedulers und des User-Space-Thread-Schedulers. Im Wesentlichen sind mehrere Threads an mehrere Kernel-Threads gebunden, wodurch die meisten Thread-Kontextwechsel stattfinden Benutzerraum und mehrere Kernel-Threads können die Prozessorressourcen vollständig nutzen.
4. Planung der Implementierung des Goroutine-Mechanismus
Der Goroutine-Mechanismus implementiert das M:N-Thread-Modell und der Goroutine-Mechanismus ist Coroutine An Implementierung des integrierten Schedulers von Golang, der es jeder CPU in einer Multi-Core-CPU ermöglicht, eine Coroutine auszuführen.
Der Schlüssel zum Verständnis der Prinzipien des Goroutine-Mechanismus liegt darin, die Implementierung des Go-Sprachplaners zu verstehen.
So funktioniert der Scheduler
Es gibt vier wichtige Strukturen in der Go-Sprache, die die gesamte Scheduler-Implementierung unterstützen, nämlich M, G, P und Sched Drei Definitionen sind in runtime.h und Sched ist in proc.c definiert.
Sched-Struktur ist der Scheduler, der Warteschlangen verwaltet, in denen M und G sowie einige Statusinformationen des Schedulers gespeichert sind.
M-Struktur ist Maschine, System-Thread, der vom Betriebssystem verwaltet wird. M ist eine große Struktur, die einen kleinen Objektspeicher-Cache (mcache) verwaltet. Es gibt viele Informationen über Goroutinen, Zufallszahlengeneratoren usw.
P-Struktur ist Prozessor, sein Hauptzweck ist die Ausführung von Goroutinen. Es verwaltet eine Goroutine-Warteschlange, nämlich Runqueue. Der Prozessor ist ein wichtiger Teil, der uns den Übergang von der N:1-Planung zur M:N-Planung ermöglicht.
G ist die Kernstruktur der Goroutine-Implementierung. Sie enthält den Stapel, den Befehlszeiger und andere wichtige Informationen für die Planung von Goroutine, wie z. B. den blockierten Kanal.
Die Anzahl der Prozessoren wird beim Start auf den Wert der Umgebungsvariablen GOMAXPROCS gesetzt oder durch Aufruf der Funktion GOMAXPROCS() zur Laufzeit festgelegt. Die feste Anzahl von Prozessoren bedeutet, dass zu jedem Zeitpunkt nur GOMAXPROCS-Threads Go-Code ausführen.
Wir verwenden Dreiecke, Rechtecke und Kreise, um Maschinenprozessor bzw. Goroutine darzustellen.
Im Szenario eines Single-Core-Prozessors laufen alle Goroutinen im selben M-System-Thread. Jeder M-System-Thread verwaltet einen Prozessor ist nur eine Goroutine und andere Goroutinen warten in der Runqueue. Nachdem eine Goroutine die Ausführung ihrer eigenen Zeitscheibe beendet hat, gibt sie den Kontext auf und kehrt zur Runqueue zurück. Im Szenario von Mehrkernprozessoren enthält jeder M-Systemthread einen Prozessor, um Goroutinen auszuführen.
Unter normalen Umständen plant der Scheduler gemäß dem oben genannten Prozess, aber Threads werden blockiert. Schauen Sie sich die Handhabung von Thread-Blockierungen durch Goroutine an.
Thread-Blockierung
Wenn die laufende Goroutine blockiert wird, z. B. durch einen Systemaufruf, wird ein weiterer Systemthread (M1) erstellt und der aktuelle M-Thread Geben Sie seinen Prozessor auf, P wird zur Ausführung an einen neuen Thread übergeben.
Runqueue-Ausführung ist abgeschlossen
Wenn eine der Runqueues des Prozessors leer ist, kann keine Goroutine geplant werden. Es wird die Hälfte der Goroutine aus einem anderen Kontext stehlen.
Weitere Gedanken zur Parallelitätsimplementierung
Es gibt viel zu lernen über die Parallelität Besprochen, wie der Unterschied zwischen der Go-Sprache und der Scala-Parallelitätsimplementierung, der Vergleich zwischen Golang CSP und dem Actor-Modell usw.
Das Verständnis dieser Implementierungen von Parallelitätsmechanismen kann uns helfen, gleichzeitige Programme besser zu entwickeln und die Leistung zu optimieren.
Bei den drei Multithreading-Modellen können Sie auf die Implementierung der Java-Sprache achten.
Wir wissen, dass Java die Unterschiede im zugrunde liegenden Betriebssystem durch die JVM kapselt und verschiedene Betriebssysteme möglicherweise unterschiedliche Threading-Modelle verwenden. Beispielsweise können Linux und Windows ein Eins-zu-Eins-Modell verwenden Versionen von Solaris und Unix verwenden möglicherweise ein Viele-zu-Viele-Modell. Die JVM-Spezifikation schreibt keine spezifische Implementierung des Multithreading-Modells vor. Alle Modelle 1:1 (Kernel-Threads), N:1 (Benutzerstatus-Threads) und M:N (gemischt) sind akzeptabel. Wenn es um das Multithreading-Modell der Java-Sprache geht, muss es für eine bestimmte JVM implementiert werden. Beispielsweise verwendet die HotSpot-VM von Oracle/Sun standardmäßig das 1:1-Threading-Modell.
Das obige ist der detaillierte Inhalt vonDetaillierte grafische Erläuterung des Parallelitätsmechanismus der Go-Sprache. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!