Einmalige Rückgabe eines Wertes?

muckelzwerg

Bekanntes Mitglied
Da ich sowas gerade zurechtgefummelt habe, stell ich das mal zur Diskussion.
Ihr habt ein Objekt, mit einem Attribut, das ihr an ein anderes Objekt übergeben wollt.
Allerdings könnt ihr die Referenz nicht verwenden, wegen unterschiedlichen Threads, oder weil ihr das Attribut übers Netzwerk verschiebt, etc.
Das Attribut wird also beim Aufruf von "get..." kopiert.

Jetzt wollt ihr das Kopieren des Attributs möglichst reduzieren, weil es "teuer" ist. (egal welche Kosten)
Von einem get()-Aufruf zum anderen, muss sich das Attribut nicht zwangsläufig verändert haben. Ihr könnt also dem Aufrufer die Verantwortung zuschieben, die letzte Kopie aufzubewahren, bis er eine Neue bekommt.
Da entsteht dann sowas wie eine "OneTimeCopy", bei der entweder das Attribut kopiert oder null zurückgegeben, oder eine Exception ausgelöst, oder irgendein anderes Signal übetragen wird.
Aufrufen -> Kopie
nochmal Aufrufen -> keine Änderung -> "null"
nochmal Aufrufen -> neue Daten -> neue Kopie

Zusätzlich gilt, dass ihr kein aktives "Senden bei Änderung" nutzen könnt. Ist technisch nicht möglich, oder kostet noch mehr ...
Ihr müsst mit wiederkehrenden Abfragen umgehen, zwischen denen sich das Attribut gar nicht, oder sogar mehrfach geändert haben kann.

zurechtgefrickelt ist sowas schnell, aber wie würdet ihr das machen "dass es sich gut anfühlt"? 😉
 
Hm, warum soll das Attribut denn per get geholt werden? (Das aller erste mal ok, aber danach nicht mehr) Der Setter soll es schreiben wenn es sich verändert.
 
Ja, allerdings wird das Attribut in verschiedenen Threads genutzt. Derjenige, der es geholt hat, arbeitet vielleicht gerade damit, während der Setter es ändert.

Typische Reaktion -> Wechselseitiger Ausschluss
Den möchte man in diesem Fall wegen Leistungsverlust nicht haben.
Aufgrund der hohen Abfrageraten ist es aber akzeptabel, wenn der Verbraucher eine alte Kopie verwendet und erst beim nächsten Aufruf die Aktualisierte bekommt.

Zu den Raten:
Die Abfragerate ist relativ konstant und liegt irgendwo zwischen 10Hz und 100Hz.
Das Attribut muss sich über längere Zeit (Minuten) gar nicht unbedingt verändern, können sich aber auch innerhalb einer Sekunde 200mal ändern.

Zu den Kardinalitäten:
Abfrager gibt es nur wenige (<10). Objekte die abgefragt werden gibt es mehr (bspw ~50).
 
Der Anrufer könnte die Referenz seiner Kopie als Parameter an die getter Methode übergeben. Ist die Kopie gleich dem Original, wird null zurückgegeben, sonst wird eine neue Kopie zurückgegeben.

Gruß,
André
 
Ja, das ist auch eine Möglichkeit.
Vielleicht sollte ich dazusagen, dass der Anbieter des Attributs weiß, wann es sich ändert (er schreibt die Änderungen ja). Er kann also auch ein "changed" Flag setzen und dem Verbraucher bei Anfrage den neuen Wert geben.
(Sorry, war für mich natürlich klar.)

Soweit ganz gut. Aber was passiert nun, wenn wir die Verbraucherzahl auf >1 erhöhen.
Entscheidet das Objekt selbst, wenn es eine neue Kopie rausgibt, dann müsste es sich die Verbraucher merken.
Sonst kommt C1 und holt eine Kopie, das Objekt geht auf "unchanged" und C2 bekommt "null", obwohl er noch keine Kopie hat.

Da ist Deine Variante mit dem Referenzvergleich wieder besser, weil C2 dann "null" übergibt und trotz unveränderter Werte eine Kopie bekommt.
Weniger schön ist, dass die Objekte dann verglichen werden müssen. Das macht auch wieder relativ viel Aufwand. (Sidequest: Wieviel Aufwand im Vergleich zum kopieren?)
Und wenn jeder Verbraucher jedes Objekt anfragt, haben wir #Verbraucher * #Objekte Kopien im Speicher rumliegen.
Bei 10 Verbrauchern und 100 Objekten, sind das schon 1000 kopierte Attribute.
Ist auch wieder nicht ganz so schön.
 
Ja, allerdings wird das Attribut in verschiedenen Threads genutzt. Derjenige, der es geholt hat, arbeitet vielleicht gerade damit, während der Setter es ändert.

Aehm - das Problem hast du aber auch in umgekehrter Richtung - Einer will es lesen während es ein Anderer gerade bearbeitet 🙂 Um gegenseitigen Ausschluss wirst du nicht herumkommen.

IMO ist pollen über einen teuren Kanal schlechter als putten ... so rein per Definition - es gibt unnötigen traffic.

Ich habe eine sehr ähnliches Problem schon einmal gelöst:
Konkret behandelt habe ich das Problem in einem Kommunikations-Proxy. Da gab es zig setter (um zu senden) für alle möglichen Messagetypen und jede hat zuerst die Message geschrieben, dann unter Schutz ein Flag dass eine neue Meldung da ist.

Dieses Flag schützte auch vor überschreiben der Message, falls die noch nicht abgeholt wurde - es gab aber auch Messages die überschrieben werden durften - das wusste nur die Message selbst.

Um zu senden wurde die Meldung (natürlich wieder unter Schutz) kopiert.

(Empfangsseitig sah es ähnlich aus, einfach mit gettern)

Ziel war natürlich, dass der teure Kanal so optimal wie möglich ausgenutzt war.
 
Zuletzt bearbeitet:
Ich sag auch nicht, dass es eine universelle Lösung für jede denkbare Variante gibt.
Wenn ich einen oder mehrere Verbraucher habe, die alle eine eigene Kopie besitzen, dann können sie mit der Kopie arbeiten, wie sie wollen.
Wobei ich mit "arbeiten" in erster Linie lesen und nicht schreiben meine, da der Anbieter die Hoheit über die Werte hat. Es bringt dem Verbraucher ja nichts, die Werte seiner Kopie zu ändern, wenn sie niemals zurückgeschrieben werden.

Jetzt hör ich Dich gleich sagen "Wenn sie alle nur lesen, brauchst Du auch die Kopie nicht".
Leider ist das Attribut im Original nicht threadsafe. Wenn einer der Verbraucher bspw. eine Anzeige mit den Werten des Attributs füllen will und der Anbieter gerade die Werte ändert ... krachts.
Desweiteren sind veraltete Zustände in Ordnung, ein gemischter Zustand, der bei gleichzeitigem Zugriff auf Listen entstehen könnte, aber nicht.



Momentan bin ich bei folgender Variante.
Der Anbieter besitzt einmal das Originalattribut und eine "interne Kopie".
Wird das Attribut angefragt, dann überprüft er, ob die Kopie noch aktuell ist.
Ist sie das, gibt er die Referenz der Kopie heraus. Ist sie es nicht, erzeugt er eine funktionslokale neue Kopie und weist diese dann der internen Kopie zu. Anschließend gibt er wieder die Referenz heraus.
Das reduziert die Kopien auf die Anzahl der Objekte und erlaubt einen mehrfachen, gleichzeitigen Zugriff.


In meinem Fall ist das originalAttribut jedoch eine HashMap und die Kopie ein Vector.
Der Vector ist "threadsafe", aber das garantiert mir nur, dass es nicht raucht. Es garantiert nicht, dass ich immer einen konsistenten Zustand bekomme.
Deswegen die funktionslokale Kopie mit anschließender Zuweisung.
Bei mehreren Verbrauchern in unterschiedlichen Threads könnten dann gleichzeitig mehrere funktionslokale Kopien erzeugt und die interne Kopie mehrfach überschrieben werden. Das wäre soweit kein Problem, da bei jedem Überschreiben immer eine konsistente Kopie zugewiesen wird.

Leider verschiebe ich damit das Konsistenzproblem nur zur HashMap, die mir da keine Garantien machen kann.
Wenn der Verbraucher das Attribut anfragt, kann es sein, dass der Anbieter gerade neue Messdaten in die HashMap einträgt.
Gleichzeitig liest aber der Verbraucher ebenfalls die Hashmap aus einem anderen Thread heraus, indem er die Get-Methode ausführt.
Ist das changed-Flag gesetzt, dann wird aus der HashMap ein neuer Vektor kopiert. Der kann nun wieder inkonsistent sein.
Auch das sollte sich wieder mit einer funktionslokalen Kopie und Zuweisung beheben lassen, ohne Synchronization verwenden zu müssen.
(Der Anbieter ist IMMER singlethreaded)


So und jetzt noch zum "Put". 😉 Das Problem ist, dass sich bei manchen Objekten das Attribut extrem schnell ändert. 200Hz und mehr.
Wenn ich da jedesmal eine Kopie mache, ist das eine ziemliche Verschwendung, da die Kopien noch mehrfach überschrieben werden, bevor ein Verbraucher sie wieder abfragt.
Die Verbraucher kommen zwar einigermaßen regelmäßig, aber die Anbieter wissen nicht genau wann. Sie können also nur über die Abfrage erfahren, ob das Attribut gewünscht wird und in dem Moment schauen, ob die Kopie aktualisiert werden muss.


Letztlich bliebe noch statt der HashMap (ist hier eh nur ein Beispiel) einen wirklich sicheren Container zu verwenden, so dass man die Referenz rausgibt und alle Verbraucher lesen, wie es ihnen passt.
Das ist aber auch wieder nicht gewünscht, weil damit die Kontrolle über das Originalattribut abgegeben wird und Veränderungen nicht mehr überprüft werden können. Der Anbieter kontrolliert intern und sorgt dafür, dass er nur legale Werte einträgt.
Gibt er die Referenz auf das Original heraus, kann da wieder viel anstrengendes Zeug passieren.


Na, spielt noch jemand mit? 😉
 
Zuletzt bearbeitet:
Damit caste ich die Referenz auf eine abgeleitete MapKlasse, die bei Schreiboperationen eine Exception wirft.
Hm ...
Die Verbraucher sind nicht in der Lage direkt mit der Map zu arbeiten. Unter anderem werden damit JTables gefüllt.
Wenn ich die Kopie komplett spare will, müsste ich noch ein TableModel für die HashMap bauen. (oder klauen ^^)

Was ich jetzt nicht sehe (vielleicht übersehen), wie das mein Konsistenzproblem löst. Wenn ich während des Füllens der Map zugreife, bekomme ich dennoch wieder einen unerlaubten Zustand. Und den bekomm ich dann wieder nur mit Synchronisation oder einer Kopie weg.

Nehmen wir an ich verwende die unmodifiable HashMap direkt, ohne Vektor oder Kopie.
Dann müsste ich ein Lock auf das Lesen setzen und das auch noch in einem TableModel verwenden ... riecht noch blöder.

Wie ist Folgendes:
Eine lokale Referenz auf die HashMap, gecastet als unmodifyable. Diese Referenz ruft der Verbraucher nun JEDESMAL ab, wenn er das Attribut verwenden will.
Der Anbieter schreibt neue Werte erst in eine funktionslokale Kopie. Das kostet dann einen zusätzlichen Konstruktor.
Nach dem Schreiben der Werte aktualisiert er die unmodifiable Referenz.
Wenn ein Verbraucher beim Lesen unterbrochen wird, dann schreibt der Anbieter in das zweite Objekt. Selbst wenn er durchläuft und die Referenz aktualisiert, bekommt der Verbraucher davon nichts mit, weil er seine Referenz auf das alte Objekt verweist. (Call by Value of Reference quasi) Damit liest er einen konsistenten Zustand, auch wenn er suspendiert wird und der Anbieter die Werte ändert.

Was mir daran noch nicht gefällt ist, dass der Verbraucher die alte Version am Leben erhält und erst an die GC übergibt, wenn er neu anfragt.
 

Neue Themen


Zurück
Oben