Heim  >  Artikel  >  Backend-Entwicklung  >  Über die Implementierung des Golang-Kanals

Über die Implementierung des Golang-Kanals

藏色散人
藏色散人nach vorne
2021-05-06 10:00:172286Durchsuche

Das Folgende ist eine Einführung in die Implementierung des Golang-Kanals aus der Golang-Tutorial-Kolumne. Ich hoffe, dass es für Freunde in Not hilfreich sein wird!

1. Einführung

Kanal ist eine Kommunikationsmethode zwischen Goroutinen, die auf Sprachebene bereitgestellt wird. Kanäle können zum Übertragen von Nachrichten zwischen zwei oder mehr Goroutinen verwendet werden. Der Kanal ist eine prozessinterne Kommunikationsmethode, sodass der Prozess der Übergabe von Objekten über Kanäle mit dem Parameterübergabeverhalten beim Aufrufen von Funktionen übereinstimmt. Beispielsweise können auch Zeiger übergeben werden. Wenn eine prozessübergreifende Kommunikation erforderlich ist, wird empfohlen, zur Lösung des Problems ein verteiltes System zu verwenden, beispielsweise die Verwendung von Kommunikationsprotokollen wie Socket oder HTTP.

Der Kanal ist typbezogen, das heißt, ein Kanal kann nur einen Werttyp übergeben, und dieser Typ muss bei der Kanaldeklaration angegeben werden. Beachten Sie, dass der Kanal selbst auch ein nativer Typ in der Go-Sprache ist und denselben Status wie Typen wie Map hat, sodass der Kanal selbst nach der Definition auch durch den Kanal geleitet werden kann.

2. Zugrunde liegende Implementierung

2.1 hchan-Struktur

type hchan struct {
   qcount   uint           // 队列中当前数据的个数
   dataqsiz uint           // size of the circular queue
   buf      unsafe.Pointer // 数据缓冲区,存放数据的环形数组
   elemsize uint16 // channel中数据类型的大小(单个元素的大小)
   closed   uint32 // 表示channel是否关闭标识位
   elemtype *_type // 队列中的元素类型
   sendx    uint   // 当前发送元素的索引
   recvx    uint   // 当前接收元素的索引
   recvq    waitq  // 接受等待队列,由recv行为(也就是<-ch)阻塞在channel上的goroutine队列
   sendq    waitq  // 发送等待队列, 由send行为(也就是ch<-)阻塞在channel上的goroutine队列
   //lock保护chann中的所有字段,以及在此通道上阻塞的sudoG中的几个字段。
   //保持此锁时不要更改另一个G状态(特别是没准备好G),因为这可能会因堆栈收缩而死锁
   lock mutex
}

//发送及接收队列的·1结构体
type waitq struct {
    first *sudog
    last  *sudog
}
  • qcount uint // Die Anzahl der verbleibenden Elemente in der aktuellen Warteschlange.
  • dataqsiz uint // Länge der Ringwarteschlange, also die Größe des Puffers, also make(chan T, N), N.
  • buf unsafe.Pointer // Ring-Warteschlangenzeiger.
  • elemsize uint16 // Die Größe jedes Elements.
  • closed uint32 // Gibt an, ob der aktuelle Kanal geschlossen ist. Wenn ein Kanal erstellt wird, wird dieses Feld auf 0 gesetzt, was bedeutet, dass der Kanal geöffnet ist; wenn es durch Aufrufen von close auf 1 gesetzt wird, wird der Kanal geschlossen.
  • elemtype *_type // Elementtyp, der zur Zuordnung bei der Datenübertragung verwendet wird.
  • sendx uint und recvx uint sind die Statusfelder des Ringpuffers, die den aktuellen Index des Puffers anzeigen – unterstützende Arrays, von denen er Daten senden und empfangen kann.
  • recvq waitq // Goroutine-Warteschlange wartet darauf, Nachrichten zu lesen.
  • sendq waitq // Goroutine-Warteschlange wartet darauf, Nachrichten zu schreiben.
  • lock mutex // Mutex-Sperre, sperrt den Kanal für jeden Lese- und Schreibvorgang, da Senden und Empfangen sich gegenseitig ausschließen müssen. 2.2 Erstellungsprozess
2.2.1 Schreibvorgang

3.3 Schreiben Sie den Eintrag Der Prozess ist wie folgt:

Sperren Sie die gesamte Pipeline-Struktur.

Bestätigen Sie den Schreibvorgang, warten Sie auf die Goroutine aus der Warteschlange und schreiben Sie das Element dann direkt in die Goroutine.

Wenn recvq leer ist, ermitteln Sie, ob der Puffer verfügbar ist. Kopiert, sofern verfügbar, Daten aus der aktuellen Goroutine in den Puffer.

Wenn der Puffer voll ist, wird das zu schreibende Element in der aktuell ausgeführten Goroutine-Struktur gespeichert und die aktuelle Goroutine wird in sendq in die Warteschlange gestellt und von der Ausführung abgehalten.

Die Sperre wird aufgehoben, wenn der Schreibvorgang abgeschlossen ist.

2.2.2 Lesevorgang

  • Lesen Sie zuerst die globale Kanalsperre.
  • Versuchen Sie sendq, um die wartende Goroutine aus der Warteschlange zu holen.
  • Wenn eine wartende Goroutine und ein Puffer vorhanden sind (der Puffer ist voll), nehmen Sie die Daten vom Kopf der Pufferwarteschlange und entnehmen Sie dann eine Goroutine aus sendq. Speichern Sie die Daten in der Goroutine im Pufferbit und beenden Sie den Lesevorgang, um die Sperre aufzuheben.
  • Wenn keine wartende Goroutine vorhanden ist und sich Daten im Puffer befinden, lesen Sie die Pufferdaten direkt und erklären Sie die Lesefreigabesperre.
Wenn keine wartende Goroutine vorhanden ist und kein Puffer vorhanden ist oder der Pufferbereich leer ist, fügen Sie die aktuelle Goroutine zur Denq-Warteschlange hinzu, geben Sie den Ruhezustand ein und warten Sie, bis Sie durch Schreiben von Goroutine geweckt werden. Beenden Sie das Lösen der Sperre.

Das obige ist der detaillierte Inhalt vonÜber die Implementierung des Golang-Kanals. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:csdn.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen
Vorheriger Artikel:So verwenden Sie Iota in GoNächster Artikel:So verwenden Sie Iota in Go