Semaphore in Golang

Semaphore In Golang



Abgesehen von einem wirklich coolen Maskottchen ist die Unterstützung der Parallelität eines der praktischsten und leistungsstärksten Features der Programmiersprache Go.

Wenn es um Leistung und effiziente Nutzung der verfügbaren Ressourcen in modernen Systemen geht, ist Go unübertroffen. Fragen Sie Docker.

Wenn es um gleichzeitige Anwendungen geht, ist die Verwaltung des Zugriffs auf gemeinsam genutzte Ressourcen eine der größten Herausforderungen. Glücklicherweise gibt es schon seit Ewigkeiten ein sogenanntes Semaphor, das dabei helfen kann, den Zugriff auf eine gemeinsame Ressource zwischen mehreren Threads oder Goroutinen zu verwalten.







In diesem Tutorial werden wir die Funktionsweise und Implementierung von Semaphoren in Go untersuchen. Wir beginnen mit dem, was Semaphoren sind, und geben dann Beispiele, wie man sie in Go erstellt und verwendet.



Was sind Semaphoren?

Beginnen wir mit den Grundlagen.



Ein Semaphor ist ein leistungsstarker und nützlicher Synchronisationsmechanismus, der eine Zählung und zwei Operationen verwaltet:





  1. Ein Warten
  2. Ein Signal

In Semaphor ist eine Anzahl im Grunde eine Zahl, die die Anzahl der Ressourcen angibt.

Wenn eine bestimmte Goroutine auf die gemeinsam genutzte Ressource (geschützt durch das Semaphor) zugreifen möchte, muss die Goroutine „Wait“ aufrufen. Wenn Ressourcen verfügbar sind, verringert die Operation „Warten“ die Anzahl der Ressourcen und ermöglicht der Goroutine die Verarbeitung.



Wenn die Anzahl der Ressourcen jedoch Null erreicht, blockieren alle nachfolgenden „Wait“-Aufrufe alle Goroutinen daran, fortzufahren, bis die Ressourcen verfügbar werden.

Sobald eine Goroutine die Aufgabe mit der Ressource abgeschlossen hat, übernehmen die „Signal“-Operationen die Aufgabe, den Zähler zu erhöhen, um anzuzeigen, dass die Ressource für den Erwerb durch eine andere Goroutine verfügbar ist.

Erstellen Sie ein Semaphor in Go

In Go können wir mithilfe eines gepufferten Kanals ein Semaphor erstellen. Ein gepufferter Kanal der Größe „N“ kann als Semaphor mit der Anzahl „N“ Ressourcen fungieren.

Schauen Sie sich den folgenden Code an:

Paket hauptsächlich
Funktion hauptsächlich () {
Semaphor := machen ( Chan Struktur {}, 5 )
verschieben schließen ( Semaphor )
}

In diesem Beispiel erstellen wir einen gepufferten Kanal namens „Semaphore“ mit der Größe 5. Das bedeutet, dass er fünf Ressourcen enthält.

Um eine Ressource freizugeben, können wir eine leere Struktur in den Kanal senden.

Um eine Ressource zu erwerben, erhalten wir sie einfach vom Kanal.

Semaphore steuern den Zugriff auf eine Ressource

Wenn es um die reale Welt geht, wird es natürlich kaum ein einfaches Semaphor geben. Eine der Aufgaben eines Semaphors ist die Steuerung der Ressourcen.

Angenommen, wir verfügen über eine begrenzte Anzahl von Ressourcen, auf die mehrere Goroutinen gleichzeitig zugreifen müssen.

Um die Anzahl der Goroutinen zu steuern, die gleichzeitig darauf zugreifen können, können wir eine Semaphore verwenden, wie im folgenden Beispiel gezeigt:

Paket hauptsächlich
importieren (
„fmt“
„synchronisieren“
Funktion Arbeiter ( Ausweis int , Semaphor Chan Struktur {}) {
fmt . Druckenf ( „Arbeiter %d wartet auf eine Genehmigung \N ' , Ausweis )
< - Semaphor
verschieben Funktion () {
fmt . Druckenf ( „Arbeiter %d hat die Genehmigung freigegeben \N ' , Ausweis )
Semaphor < - Struktur {}{}
}(
fmt . Druckenf ( „Arbeiter %d arbeitet \N ' , Ausweis )
}
Funktion hauptsächlich () {
Semaphor := machen ( Chan Struktur {}, 5 )
für ich := 0 ; ich < 5 ; ich ++ {
Semaphor < - Struktur {}{}
}
verschieben schließen ( Semaphor )
War WG-Synchronisierung . Wartegruppe
für ich := 1 ; ich < = 5 ; ich ++ {
wg . Hinzufügen ( 1 )
gehen Funktion ( Ausweis int ) {
verschieben wg . Erledigt ()
Arbeiter ( Ausweis , Semaphor )
}( ich )
}
wg . Warten ()
}

Im gegebenen Beispiel deklarieren wir eine Worker-Funktion, die eine Aufgabe simuliert, die von einer Goroutine ausgeführt wird. Die Funktion akzeptiert zwei Parameter: eine ID zur Identifizierung des Workers und ein Semaphor, das den Kanal darstellt, der den Zugriff auf die Ressource steuert.

In der Funktion haben wir die „Sende“-Operation an den Kanal, der mit <- Semaphore gekennzeichnet ist. Wenn der Kanal leer ist, blockiert der Worker und wartet. Es wartet, bis es eine Ressource vom Kanal erhält.

Wir richten auch eine verzögerte Funktion ein, die ausgeführt wird, wenn die Funktion zurückkehrt. Sobald dies erledigt ist, sendet es eine leere Struktur an den Kanal, um die Ressource freizugeben.

Das Ausführen des angegebenen Codes gibt Folgendes zurück:

Arbeiter 1 wartet für eine Erlaubnis
Arbeiter 4 wartet für eine Erlaubnis
Arbeiter 4 funktioniert
Arbeiter 4 hat die Genehmigung freigegeben
Arbeiter 3 wartet für eine Erlaubnis
Arbeiter 3 funktioniert
Arbeiter 1 funktioniert
Arbeiter 1 hat die Genehmigung freigegeben
Arbeiter 5 wartet für eine Erlaubnis
Arbeiter 5 funktioniert
Arbeiter 5 hat die Genehmigung freigegeben
Arbeiter 3 hat die Genehmigung freigegeben
Arbeiter 2 wartet für eine Erlaubnis
Arbeiter 2 funktioniert
Arbeiter 2 hat die Genehmigung freigegeben

Dies zeigt ein sehr gutes Beispiel für ein Semaphor, das gemeinsam genutzte Ressourcen für mehrere Goroutinen steuert, die versuchen, gleichzeitig auf die Ressourcen zuzugreifen und diese zu nutzen.

Abschluss

In diesem Tutorial haben wir eines der leistungsstärksten Konstrukte kennengelernt, wenn es um Parallelität geht: Semaphore unter Verwendung von Kanälen in der Programmiersprache Go. Seien Sie vorsichtig bei Deadlocks, wie bei allem, was mit Parallelität zu tun hat.