Golang context.WithValue: wie mehrere Schlüssel / Wert-Paare hinzugefügt werden

8

Mit Gos context -Paket ist es möglich, anforderungsspezifische Daten mit

an den Stapel von Anforderungshandhabungsfunktionen zu übergeben %Vor%

Dies erstellt ein neues Context , das eine Kopie des Elternteils ist und den Wert val enthält, auf den mit dem Schlüssel zugegriffen werden kann.

Wie gehe ich vor, wenn ich mehrere Schlüssel / Wert-Paare in Context speichern möchte? Soll ich WithValue() mehrmals aufrufen, jedes Mal, wenn ich den Context von meinem letzten Aufruf an WithValue() übergebe? Dies erscheint umständlich.
Oder soll ich eine Struktur verwenden und alle meine Daten dorthin legen, z. Ich muss nur einen Wert (die Struktur) übergeben, von dem auf alle anderen zugegriffen werden kann.

Oder gibt es eine Möglichkeit, mehrere Schlüssel / Wert-Paare an WithValue() ?

zu übergeben     
alex 02.11.2016, 12:28
quelle

2 Antworten

9

Sie haben Ihre Optionen ziemlich genau aufgelistet. Die Antwort, die Sie suchen, hängt davon ab, wie Sie die im Kontext gespeicherten Werte verwenden möchten.

context.Context ist ein unveränderliches Objekt, das "Erweitern" mit einem Schlüssel / Wert-Paar ist nur möglich durch eine Kopie davon erstellen und der Kopie den neuen Schlüsselwert hinzufügen (was unter der Haube geschieht, von der context Paket).

Möchten Sie, dass weitere Handler auf alle Werte transparent zugreifen können? Fügen Sie dann alle in einer Schleife hinzu, indem Sie immer den Kontext der letzten Operation verwenden.

Eine Sache, die hier zu beachten ist, ist, dass context.Context kein map unter der Haube verwendet, um die Schlüssel / Wert-Paare zu speichern, was zunächst überraschend klingen mag, aber nicht, wenn Sie darüber nachdenken müssen unveränderlich und sicher für die gleichzeitige Verwendung.

Verwenden Sie map

Wenn Sie zum Beispiel viele Schlüssel / Wert-Paare haben und Werte mit den Schlüsseln schnell suchen müssen, führt das separate Addieren zu einem Context , dessen Methode Value() langsam ist . In diesem Fall ist es besser, wenn Sie alle Schlüssel / Wert-Paare als einen einzigen map -Wert hinzufügen, auf den über Context.Value() zugegriffen werden kann, und jeder darin enthaltene Wert kann mit dem zugehörigen Schlüssel in O(1) time abgefragt werden. Wissen Sie, dass dies für die gleichzeitige Verwendung nicht sicher ist, da eine Karte von gleichzeitigen Göroutinen modifiziert werden kann.

Verwenden Sie struct

Wenn Sie einen großen Wert für struct verwenden, der Felder für alle Schlüssel / Wert-Paare enthält, die Sie hinzufügen möchten, ist dies möglicherweise ebenfalls eine praktikable Option. Der Zugriff auf diese Struktur mit Context.Value() würde Ihnen eine Kopie der Struktur zurückgeben, so dass sie für die gleichzeitige Verwendung sicher wäre (jede Goroutine könnte nur eine andere Kopie erhalten), aber wenn Sie viele Schlüssel / Wert-Paare haben, würde dies dazu führen unnötige Kopie einer großen Struktur jedes Mal, wenn jemand ein einzelnes Feld davon benötigt.

Verwenden Sie eine hybride Lösung

Eine hybride Lösung könnte sein, alle Schlüssel-Wert-Paare in ein map zu setzen und eine Wrapper-Struktur für diese Map zu erstellen, die map (nicht-exportiertes Feld) versteckt und bereitstellt nur ein Getter für die in der Karte gespeicherten Werte. Wenn Sie nur diesen Wrapper dem Kontext hinzufügen, behalten Sie den sicheren gleichzeitigen Zugriff für mehrere Gatorutins ( map wird nicht exportiert), aber keine großen Daten müssen kopiert werden ( map -Werte sind kleine Deskriptoren ohne die Schlüsselwertdaten und immer noch schnell (schließlich indexieren Sie eine Karte).

So könnte es aussehen:

%Vor%

Verwenden Sie es:

%Vor%

Ausgabe (versuchen Sie es auf dem Gehe Spielplatz ):

%Vor%

Wenn die Leistung nicht kritisch ist (oder Sie relativ wenige Schlüssel / Wert-Paare haben), würde ich sie einzeln hinzufügen.

    
icza 02.11.2016, 12:37
quelle
6

Ja, Sie haben Recht. Sie müssen WithValue() jedesmal die Ergebnisse übergeben. Um zu verstehen, warum es so funktioniert, lohnt es sich, ein wenig über die Theorie hinter dem Kontext nachzudenken.

Ein Kontext ist eigentlich ein Knoten in einem Baum von Kontexten (daher nehmen die verschiedenen Kontextkonstruktoren einen "Eltern" -Kontext an). Wenn Sie einen Wert aus einem Kontext anfordern, fordern Sie tatsächlich den ersten gefundenen Wert an, der mit Ihrem Schlüssel übereinstimmt, wenn Sie in der Baumstruktur nach dem entsprechenden Kontext suchen. Dies bedeutet, dass Sie einen anderen Wert finden können, wenn Ihr Baum mehrere Zweige hat oder Sie an einem höheren Punkt in einem Zweig beginnen. Dies ist Teil der Kraft von Kontexten. Storno-Signale hingegen verteilen den Baum auf alle untergeordneten Elemente des abgebrochenen, so dass Sie einen einzelnen Zweig abbrechen oder den gesamten Baum abbrechen können.

Hier ein Beispiel für einen Kontextbaum, der verschiedene Dinge enthält, die Sie möglicherweise in Kontexten speichern:

Die schwarzen Kanten stellen Datenabfragen dar und die grauen Kanten stellen Abbruchsignale dar. Beachten Sie, dass sie in entgegengesetzte Richtungen propagieren.

Wenn Sie eine Karte oder eine andere Struktur verwenden würden, um Ihre Schlüssel zu speichern, würde es eher den Kontext sprengen. Sie könnten nicht mehr nur einen Teil einer Anfrage stornieren, oder z. ändern Sie, wo die Dinge protokolliert wurden, je nachdem, in welchem ​​Teil der Anfrage Sie waren, usw.

TL; DR - Ja, rufe WithValue mehrmals auf.

    
Sam Whited 02.11.2016 14:27
quelle

Tags und Links