suchen
HeimBackend-EntwicklungGolangGo Channel Unlocked: So funktionieren sie

Ausführlicher Golang-Kanal: Implementierungsprinzipien und Vorschläge zur Leistungsoptimierung

Golangs Kanal ist eine Schlüsselkomponente seines CSP-Parallelitätsmodells und eine Brücke für die Kommunikation zwischen Goroutinen. Channel wird in Golang häufig verwendet und es ist wichtig, ein tiefes Verständnis seiner internen Implementierungsprinzipien zu haben. In diesem Artikel wird die zugrunde liegende Implementierung von Channel basierend auf dem Go 1.13-Quellcode analysiert.

Grundlegende Nutzung des Kanals

Bevor wir die Implementierung von Channel offiziell analysieren, werfen wir einen Blick auf seine grundlegende Verwendung:

package main
import "fmt"

func main() {
    c := make(chan int)

    go func() {
        c <- 1 // 发送操作
    }()

    x := <-c // 接收操作
    fmt.Println(x)
}

Dieser Code zeigt zwei grundlegende Operationen von Channel:

  • Sendevorgang: c
  • Empfangsvorgang: x :=

Der Kanal ist in gepufferten Kanal und nicht gepufferten Kanal unterteilt. Der obige Code verwendet einen nicht gepufferten Kanal. Wenn in einem nicht gepufferten Kanal derzeit keine andere Goroutine Daten empfängt, blockiert der Absender die Sendeanweisung.

Sie können die Puffergröße beim Initialisieren des Kanals angeben. Beispielsweise gibt make(chan int, 2) die Puffergröße auf 2 an. Bevor der Puffer voll ist, kann der Sender Daten ohne Blockierung senden, ohne darauf warten zu müssen, dass der Empfänger bereit ist. Wenn der Puffer jedoch voll ist, blockiert der Absender trotzdem.

Grundliegende Implementierungsfunktion des Kanals

Bevor Sie in den Channel-Quellcode eintauchen, müssen Sie den spezifischen Implementierungsort von Channel in Golang finden. Bei Verwendung von Channel werden tatsächlich die zugrunde liegenden Funktionen wie runtime.makechan, runtime.chansend und runtime.chanrecv aufgerufen.

Sie können den Befehl go tool compile -N -l -S hello.go verwenden, um den Code in Montageanweisungen umzuwandeln, oder das Online-Tool Compiler Explorer verwenden (zum Beispiel: go.godbolt.org/z/3xw5Cj). Durch die Analyse der Montageanleitung können wir Folgendes finden:

  • make(chan int) entspricht der Funktion runtime.makechan.
  • c entspricht der Funktion <code>runtime.chansend.
  • x := entspricht der Funktion <code>runtime.chanrecv.

Die Implementierung dieser Funktionen befindet sich in der runtime/chan.go-Datei des Go-Quellcodes.

Kanalstruktur

make(chan int) wird vom Compiler in eine runtime.makechan-Funktion umgewandelt und ihre Funktionssignatur lautet wie folgt:

func makechan(t *chantype, size int) *hchan

Unter diesen ist t *chantype der Kanalelementtyp, size int die vom Benutzer angegebene Puffergröße (0, wenn nicht angegeben) und der Rückgabewert ist *hchan. hchan ist die interne Implementierungsstruktur von Channel in Golang, definiert wie folgt:

type hchan struct {
        qcount   uint           // 缓冲区中已放入元素的数量
        dataqsiz uint           // 用户构造Channel时指定的缓冲区大小
        buf      unsafe.Pointer // 缓冲区
        elemsize uint16         // 缓冲区中每个元素的大小
        closed   uint32         // Channel是否关闭,==0表示未关闭
        elemtype *_type         // Channel元素的类型信息
        sendx    uint           // 缓冲区中发送元素的索引位置(发送索引)
        recvx    uint           // 缓冲区中接收元素的索引位置(接收索引)
        recvq    waitq          // 等待接收的Goroutine列表
        sendq    waitq          // 等待发送的Goroutine列表

        lock mutex
}

Die Attribute in hchan sind grob in drei Kategorien unterteilt:

  • Pufferbezogene Attribute: wie buf, dataqsiz, qcount usw. Wenn die Puffergröße des Kanals nicht 0 ist, wird der Puffer zum Speichern der zu empfangenden Daten verwendet und mithilfe eines Ringpuffers implementiert.
  • Wartewarteschlangenbezogene Attribute: recvq enthält Goroutine, die auf den Empfang von Daten wartet, sendq enthält Goroutine, die auf das Senden von Daten wartet. waitqImplementiert mithilfe einer doppelt verknüpften Liste.
  • Andere Attribute: wie lock, elemtype, closed usw.
Die Funktion

makechan führt hauptsächlich einige Legalitätsprüfungen und Speicherzuweisungen von Attributen wie Puffern und hchan durch, auf die hier nicht näher eingegangen wird.

Anhand einer einfachen Analyse des hchan-Attributs ist ersichtlich, dass es zwei wichtige Komponenten gibt: Puffer und Warteschlange. Alle Verhaltensweisen und Implementierungen von hchan drehen sich um diese beiden Komponenten.

Senden von Kanaldaten

Die Sende- und Empfangsprozesse von Channel sind sehr ähnlich. Analysieren Sie zunächst den Sendevorgang des Kanals (z. B. c ).

Wenn

versucht, Daten an den Kanal zu senden und die recvq-Warteschlange nicht leer ist, wird eine Goroutine, die auf den Empfang von Daten wartet, aus dem recvq-Header entnommen und die Daten werden direkt an die Goroutine gesendet. Der Code lautet wie folgt:

package main
import "fmt"

func main() {
    c := make(chan int)

    go func() {
        c <- 1 // 发送操作
    }()

    x := <-c // 接收操作
    fmt.Println(x)
}

recvq Enthält Goroutine, die auf den Empfang von Daten wartet. Wenn eine Goroutine eine Empfangsoperation verwendet (z. B. x := ) und <code>sendq zu diesem Zeitpunkt nicht leer ist, wird eine Goroutine von sendq übernommen und die Daten werden an sie gesendet.

Wenn recvq leer ist, bedeutet dies, dass zu diesem Zeitpunkt keine Goroutine auf den Empfang von Daten wartet und der Kanal versucht, die Daten in den Puffer zu legen:

func makechan(t *chantype, size int) *hchan

Die Funktion dieses Codes ist sehr einfach, er besteht darin, Daten in den Puffer zu legen. Dieser Prozess beinhaltet den Betrieb eines Ringpuffers. dataqsiz stellt die vom Benutzer angegebene Puffergröße dar (standardmäßig 0, wenn nicht angegeben).

Wenn ein nicht gepufferter Kanal verwendet wird oder der Puffer voll ist (c.qcount == c.dataqsiz), werden die zu sendenden Daten und die aktuelle Goroutine in ein sudog-Objekt gepackt, in sendq platziert und im aktuellen Goroutine wird auf Wartestatus eingestellt:

type hchan struct {
        qcount   uint           // 缓冲区中已放入元素的数量
        dataqsiz uint           // 用户构造Channel时指定的缓冲区大小
        buf      unsafe.Pointer // 缓冲区
        elemsize uint16         // 缓冲区中每个元素的大小
        closed   uint32         // Channel是否关闭,==0表示未关闭
        elemtype *_type         // Channel元素的类型信息
        sendx    uint           // 缓冲区中发送元素的索引位置(发送索引)
        recvx    uint           // 缓冲区中接收元素的索引位置(接收索引)
        recvq    waitq          // 等待接收的Goroutine列表
        sendq    waitq          // 等待发送的Goroutine列表

        lock mutex
}

goparkunlock entsperrt den Eingabe-Mutex, unterbricht die aktuelle Goroutine und versetzt sie in einen Wartezustand. gopark und goready erscheinen paarweise und sind reziproke Operationen.

Aus Sicht des Benutzers wird nach dem Aufruf von gopark die Codeanweisung zum Senden von Daten blockiert.

Kanaldatenempfang

Der Empfangsprozess von Channel ähnelt im Wesentlichen dem Sendeprozess, daher werde ich hier nicht auf Details eingehen. Die am Empfangsprozess beteiligten pufferbezogenen Vorgänge werden später ausführlich beschrieben.

Es ist zu beachten, dass der gesamte Sende- und Empfangsprozess des Kanals mit runtime.mutex gesperrt ist. runtime.mutex ist eine leichte Sperre, die häufig in laufzeitbezogenem Quellcode verwendet wird. Der gesamte Prozess ist nicht die effizienteste sperrenfreie Lösung. Es gibt ein Problem mit dem sperrfreien Kanal in Golang: go/issues#8899.

Channel-Ring-Puffer-Implementierung

Der Kanal verwendet einen Ringpuffer, um geschriebene Daten zwischenzuspeichern. Ringpuffer haben viele Vorteile und eignen sich ideal für die Implementierung von FIFO-Warteschlangen fester Länge.

Die Implementierung des Ringpuffers im Kanal ist wie folgt:

In

hchan gibt es zwei pufferbezogene Variablen: recvx und sendx. sendx stellt einen beschreibbaren Index im Puffer dar und recvx stellt einen lesbaren Index im Puffer dar. Elemente zwischen recvx und sendx stellen Daten dar, die normal in den Puffer gelegt wurden.

Go Channel Unlocked: How They Work

Sie können buf[recvx] direkt verwenden, um das erste Element der Warteschlange zu lesen, und buf[sendx] = x verwenden, um das Element am Ende der Warteschlange zu platzieren.

Pufferschreiben

Wenn der Puffer nicht voll ist, erfolgt das Einfügen von Daten in den Puffer wie folgt:

package main
import "fmt"

func main() {
    c := make(chan int)

    go func() {
        c <- 1 // 发送操作
    }()

    x := <-c // 接收操作
    fmt.Println(x)
}

chanbuf(c, c.sendx) entspricht c.buf[c.sendx]. Der obige Vorgang ist sehr einfach. Kopieren Sie einfach die Daten in den Pufferspeicherort sendx.

Dann bewegen Sie sendx zur nächsten Position. Wenn sendx die letzte Position erreicht, wird sie auf 0 gesetzt, was einem typischen End-to-End-Ansatz entspricht.

Pufferablesung

Wenn der Puffer nicht voll ist, muss sendq auch leer sein (denn wenn der Puffer nicht voll ist, wird die Goroutine, die die Daten sendet, nicht in die Warteschlange gestellt, sondern legt die Daten direkt in den Puffer). Zu diesem Zeitpunkt ist die Leselogik von Kanal chanrecv relativ einfach. Daten können direkt aus dem Puffer gelesen werden. Es handelt sich auch um einen Prozess zum Verschieben von recvx, der im Grunde derselbe ist wie das oben beschriebene Pufferschreiben.

Wenn in sendq eine Goroutine wartet, muss der Puffer zu diesem Zeitpunkt voll sein. Zu diesem Zeitpunkt lautet die Leselogik von Channel wie folgt:

func makechan(t *chantype, size int) *hchan

ep ist die Adresse, die der Variablen entspricht, die die Daten empfängt (in x := ist <code>ep beispielsweise die Adresse von x). sg stellt das erste sendq aus sudog dar. Im Code:

  • typedmemmove(c.elemtype, ep, qp) bedeutet das Kopieren des aktuell lesbaren Elements im Puffer an die Adresse der empfangenden Variablen.
  • typedmemmove(c.elemtype, qp, sg.elem) bedeutet, dass die Daten, die darauf warten, von Goroutine gesendet zu werden, in sendq in den Puffer kopiert werden. Da recv später ausgeführt wird, entspricht dies dem Platzieren der Daten in sendq am Ende der Warteschlange.

Einfach ausgedrückt kopiert der Kanal hier die ersten Daten im Puffer in die entsprechende Empfangsvariable und kopiert gleichzeitig die Elemente in sendq an das Ende der Warteschlange, wodurch FIFO (First In, First Out) implementiert wird. .

Zusammenfassung

Channel ist eine der am häufigsten genutzten Funktionen in Golang. Wenn Sie den Quellcode kennen, können Sie Channel besser nutzen und verstehen. Seien Sie gleichzeitig nicht zu abergläubisch und verlassen Sie sich auf die Leistung von Channel. Das aktuelle Design von Channel bietet noch viel Raum für Optimierung.

Optimierungsvorschläge:

  • Verwenden Sie einen leichteren Verriegelungsmechanismus oder ein sperrfreies Schema, um die Leistung zu verbessern.
  • Optimieren Sie die Pufferverwaltung und reduzieren Sie die Speicherzuweisung und Kopiervorgänge.

Leapcell: Die beste serverlose Plattform für Golang-Webanwendungen

Go Channel Unlocked: How They Work

Abschließend empfehle ich eine Plattform, die sich sehr gut für die Bereitstellung von Go-Diensten eignet: Leapcell

  1. Mehrsprachige Unterstützung: Unterstützt die Entwicklung von JavaScript, Python, Go oder Rust.
  2. Stellen Sie unbegrenzt viele Projekte kostenlos bereit: Zahlen Sie nur für das, was Sie nutzen, keine Anfragen, keine Gebühren.
  3. Extrem kostengünstig: Pay-as-you-go, keine Leerlaufgebühren. Beispiel: 25 $ unterstützt 6,94 Millionen Anfragen mit einer durchschnittlichen Antwortzeit von 60 Millisekunden.
  4. Reibungslose Entwicklererfahrung: Intuitive Benutzeroberfläche für einfache Einrichtung; vollautomatische CI/CD-Pipeline und GitOps-Integration für umsetzbare Erkenntnisse;
  5. Einfache Skalierbarkeit und hohe Leistung: Automatische Skalierung zur problemlosen Bewältigung hoher Parallelität; kein betrieblicher Aufwand, konzentrieren Sie sich auf die Erstellung.
Go Channel Unlocked: How They Work

Weitere Informationen finden Sie in der Dokumentation!

Leapcell Twitter: https://www.php.cn/link/7884effb9452a6d7a7a79499ef854afd

Das obige ist der detaillierte Inhalt vonGo Channel Unlocked: So funktionieren sie. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme
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
Golang vs. Python: Parallelität und MultithreadingGolang vs. Python: Parallelität und MultithreadingApr 17, 2025 am 12:20 AM

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.

Golang und C: Die Kompromisse bei der LeistungGolang und C: Die Kompromisse bei der LeistungApr 17, 2025 am 12:18 AM

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.

Golang vs. Python: Anwendungen und AnwendungsfälleGolang vs. Python: Anwendungen und AnwendungsfälleApr 17, 2025 am 12:17 AM

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

Golang gegen Python: Schlüsselunterschiede und ÄhnlichkeitenGolang gegen Python: Schlüsselunterschiede und ÄhnlichkeitenApr 17, 2025 am 12:15 AM

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.

Golang vs. Python: Benutzerfreundlichkeit und LernkurveGolang vs. Python: Benutzerfreundlichkeit und LernkurveApr 17, 2025 am 12:12 AM

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.

Das Performance -Rennen: Golang gegen C.Das Performance -Rennen: Golang gegen C.Apr 16, 2025 am 12:07 AM

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 vs. C: Code Beispiele und LeistungsanalyseGolang vs. C: Code Beispiele und LeistungsanalyseApr 15, 2025 am 12:03 AM

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.

Golangs Auswirkungen: Geschwindigkeit, Effizienz und EinfachheitGolangs Auswirkungen: Geschwindigkeit, Effizienz und EinfachheitApr 14, 2025 am 12:11 AM

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

See all articles

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)
1 Monate vorBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
1 Monate vorBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
1 Monate vorBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Chat -Befehle und wie man sie benutzt
1 Monate vorBy尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

WebStorm-Mac-Version

WebStorm-Mac-Version

Nützliche JavaScript-Entwicklungstools

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

EditPlus chinesische Crack-Version

EditPlus chinesische Crack-Version

Geringe Größe, Syntaxhervorhebung, unterstützt keine Code-Eingabeaufforderungsfunktion

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

VSCode Windows 64-Bit-Download

VSCode Windows 64-Bit-Download

Ein kostenloser und leistungsstarker IDE-Editor von Microsoft