Wieviel Kontrolle ist empfohlen
-
Soll die Datenstruktur jetzt plötzlich Rücksicht darauf nehmen, wenn jemand beim Thema Multithreading nicht aufgepasst hat?
Eine thread-sichere Datenstruktur? Wie komme ich denn nur auf eine derartig absurde Idee. Da muss ich wohl eingeraucht gewesen sein ..
-
wer garantiert denn, daß die Datenstruktur die Größe nicht gerade nach dem Abprüfen, ob eine exception geworfen werden soll ändert und damit der Index doch noch ungültig wird?
Und bei jedem Zugriff komplett zu locken ist zu teuer. Lesezugriffe dürfen ja parallel sein. Ergo muß man sich sowieso von außerhalb um Threadsicherheit kümmern.
Du mußt ganz offensichtlich eingraucht gewesen sein.
-
Jester schrieb:
wer garantiert denn, daß die Datenstruktur die Größe nicht gerade nach dem Abprüfen, ob eine exception geworfen werden soll ändert und damit der Index doch noch ungültig wird?
class Vector<T> { private: // ... public: T operator[int i] { T obj = data[i]; if (i >= size) { throw new IndexException(i); } return obj; } };
-
class Vector<T> { private: // ... public: T operator[int i] { T obj = data[i]; if (i >= size) { throw new IndexException(i); } return obj; } };
du kopierst das objekt erst zweimal, bevor der user es haben darf? ich würd noch nen virenscanner drüberlaufen lassen. und verwirrend ist es auch, wenn Vector<int> v;...;v[0]=0;cout<<v[0]; mal 17 anzeigt.
class Vector<T> { private: // ... T* data;//zum beispiel public: T& operator[](size_t i) { assert(i<size); //assert(i<1000000000);//je nach laune return data[i]; } };
-
exceptions` schrieb:
public: T operator[int i] { T obj = data[i]; if (i >= size) { throw new IndexException(i); } //...
Dieses if ist schon überflüssig, weil bei ungültigem Index die Zeile 3 zumindest undefiniertes Verhalten und bei nicht-trivialem Copy-Constructor eine Zugriffsverletzung erzeugt.
Außerdem ist es immer noch genausowenig threadsicher, wenn der Vektor zwischen Zeile 3 und 4 wächst.
-
Christoph schrieb:
Außerdem ist es immer noch genausowenig threadsicher, wenn der Vektor zwischen Zeile 3 und 4 wächst.
(s/wächst/schrumpft/)
gegen problem zwischen den zeilen hab ich doch schon was.
T operator[int i] { if (i >= size) { throw new IndexException(i); } T obj = data[i]; return obj; }
SCNR.
-
exceptions` schrieb:
class Vector<T> { private: // ... public: T operator[int i] { T obj = data[i]; if (i >= size) { throw new IndexException(i); } return obj; } };
Was ist, wenn zum Zeitpunkt des lesens noch Müll dort stand und erst vor der Abfrage der Größe ein sinnvoller Wert eingetragen wurde?
-
volkard schrieb:
Christoph schrieb:
Außerdem ist es immer noch genausowenig threadsicher, wenn der Vektor zwischen Zeile 3 und 4 wächst.
(s/wächst/schrumpft/)
Nein, ich meinte "wächst". Dann liest Zeile 3 nämlich noch Müll (wegen dem ungültigen Index), das if in Zeile 5 geht aber davon aus, dass der Index im Range liegt.
-
Gut, mein letzter Beitrag war ein Griff ins Klo, gebe ich zu. Ohne entsprechende Synchronisierungsmöglichkeiten (locken war ja nicht "erlaubt") gehts halt doch nicht.
volkard schrieb:
T operator[int i] { if (i >= size) { throw new IndexException(i); } return data[i]; }
Verlagert das Problem lediglich zwischen Zeile 4 und 6, nachdem nach der Überprüfung etwas entfernt werden könnte.
Ich wollte aber eigentlich mit dem Argument de thread-sicheren Datenstrukturen darauf hinaus, dass man sich diese "verbaut", wenn man eine Zugriffsmöglichkeit ohne Exceptions (Stichwort: throw()) anbietet. Für die paar Fälle in denen man den Index-Bereich "weiß" zahlt sich eine zusätzliche Zugriffsmöglichkeit nicht aus. Es besteht bei 2 Methoden aber trotzdem die Gefahr, dass Fehler verschluckt werden.
-
exceptions` schrieb:
Ich wollte aber eigentlich mit dem Argument de thread-sicheren Datenstrukturen darauf hinaus, dass man sich diese "verbaut", wenn man eine Zugriffsmöglichkeit ohne Exceptions (Stichwort: throw()) anbietet.
erklär das mal auf russisch. so versteh ichs nicht.
außerdem widerspreche ich mal prophylaktisch.
-
exceptions` schrieb:
Ich wollte aber eigentlich mit dem Argument de thread-sicheren Datenstrukturen darauf hinaus, dass man sich diese "verbaut", wenn man eine Zugriffsmöglichkeit ohne Exceptions (Stichwort: throw()) anbietet. Für die paar Fälle in denen man den Index-Bereich "weiß" zahlt sich eine zusätzliche Zugriffsmöglichkeit nicht aus.
Also bitte. Was heißt denn für dich "die paar Fälle"? Single-Threaded Programme? Daten auf die ausschließlich innerhalb eines Worker-Threads zugegriffen wird?
Und was heißt denn "zahlt sich eine zusätzliche Zugriffsmöglichkeit nicht aus"?
T at( index ) { check index return op[]( index ) }
-
volkard schrieb:
exceptions` schrieb:
Ich wollte aber eigentlich mit dem Argument de thread-sicheren Datenstrukturen darauf hinaus, dass man sich diese "verbaut", wenn man eine Zugriffsmöglichkeit ohne Exceptions (Stichwort: throw()) anbietet.
erklär das mal auf russisch. so versteh ichs nicht.
außerdem widerspreche ich mal prophylaktisch.Gut, nehmen wir an, wir haben es nun STL-like implementiert und wollen im Zuge einer Multi-Threading Anwendung dem operator[] einen Index-Range Check geben. Was machst du, wenn der Index ungültig ist?
Und was heißt denn "zahlt sich eine zusätzliche Zugriffsmöglichkeit nicht aus"?
Ich überschwemme meine öffentliche Schnittstelle ungern mit redundanten Methoden. Beide erledigen im Prinzip die selbe Aufgabe, nur eine etwas restriktiver. Warum sollte man sich also nicht einfach im Zuge einer einfachen Schnittstelle nicht auf den gemeinsamen Nenner (die restriktivere Variante) entscheiden?
-
exceptions` schrieb:
Gut, nehmen wir an, wir haben es nun STL-like implementiert und wollen im Zuge einer Multi-Threading Anwendung dem operator[] einen Index-Range Check geben. Was machst du, wenn der Index ungültig ist?
irrige annahme.
der op[] hat kein range-checking (außer mit assert) verdient.
eine at-funktion würde exceptions werfen. auch im multithreading-fall.
-
volkard schrieb:
irrige annahme.
der op[] hat kein range-checking (außer mit assert) verdient.
eine at-funktion würde exceptions werfen. auch im multithreading-fall.Was machst du dann, wenn der Index ungültig ist, UDB? Und sag mir jetzt nicht, dass du im Debug-Mode eh ein assert hast, denn wie ich bereits gesagt habe, heißt Release-Mode ja nicht per Definition, dass keine Fehler mehr auftreten können. Das klingt für mich irgendwie danach als würdest du die Fehler "verschlucken" ...
-
exceptions` schrieb:
Was machst du dann, wenn der Index ungültig ist, UDB?
jup.
Und sag mir jetzt nicht, dass du im Debug-Mode eh ein assert hast, denn wie ich bereits gesagt habe, heißt Release-Mode ja nicht per Definition, dass keine Fehler mehr auftreten können. Das klingt für mich irgendwie danach als würdest du die Fehler "verschlucken" ...
ich mach halt die programmierfehler weg.
-
volkard schrieb:
Und sag mir jetzt nicht, dass du im Debug-Mode eh ein assert hast, denn wie ich bereits gesagt habe, heißt Release-Mode ja nicht per Definition, dass keine Fehler mehr auftreten können. Das klingt für mich irgendwie danach als würdest du die Fehler "verschlucken" ...
ich mach halt die programmierfehler weg.
Ich auch, allerdings muss ich bei Exceptions beinahe blind sein, um sie zu übersehen.
-
Ich überschwemme meine öffentliche Schnittstelle ungern mit redundanten Methoden. Beide erledigen im Prinzip die selbe Aufgabe, nur eine etwas restriktiver. Warum sollte man sich also nicht einfach im Zuge einer einfachen Schnittstelle nicht auf den gemeinsamen Nenner (die restriktivere Variante) entscheiden?
Dafür wurder Vererbung erfunden. Die Basisklasse ist thread-unsafe, eine Unterklasse ist thread-save.
Oder man hat halt 2 Vectors, einer ist Thread-unsafe, der andere -safe. Dann noch ein Kopier-Kontruktor für die beiden und alles ist wunderbarPS: Juhu 800 Beiträge
-
exceptions` schrieb:
Ich überschwemme meine öffentliche Schnittstelle ungern mit redundanten Methoden. Beide erledigen im Prinzip die selbe Aufgabe, nur eine etwas restriktiver. Warum sollte man sich also nicht einfach im Zuge einer einfachen Schnittstelle nicht auf den gemeinsamen Nenner (die restriktivere Variante) entscheiden?
Ach ja. Hälst du z.B. std::find auch für redundant? Macht ja im Prinzip das gleiche wie std::find_if... oder std::stable_sort? Nein, halt, der "gemeinsame Nenner" wäre ja std::stable_sort, also sollte man wohl std::sort rausschmeißen, was?
-
DEvent schrieb:
Dafür wurder Vererbung erfunden. Die Basisklasse ist thread-unsafe, eine Unterklasse ist thread-save.
Oder man hat halt 2 Vectors, einer ist Thread-unsafe, der andere -safe. Dann noch ein Kopier-Kontruktor für die beiden und alles ist wunderbar.Dafür hat man wenn schon Schnittstellen zu verwenden und keine Vererbung (oder willst du behaupten eine thread-sichere Datenstruktur ist eine thread-unsichere Datenstruktur - IS-A Principle), geht aber sowieso völlig am Thema vorbei. Threads wurden lediglich deswegen in die Diskussion miteingebunden gebracht um am Argument "man muss nicht immer den Index prüfen" zu rütteln.
Ach ja. Hälst du z.B. std::find auch für redundant? Macht ja im Prinzip das gleiche wie std::find_if... oder std::stable_sort? Nein, halt, der "gemeinsame Nenner" wäre ja std::stable_sort, also sollte man wohl std::sort rausschmeißen, was?
Also der Vergleich ist ja jetzt schon etwas weit hergeholt und das weißt du auch selbst ..
Ich denke aber langsam lässt die Wirkung der Drogen nach und ich sehe ein, dass ein Range-Check inkl. Exception untragbar viel Performance verbrauchen würde. In diesem Falle ist es natürlich absolut gerechtfertigt sich nicht gegen "undefined behaviour" abzusichern.
-
exceptions` schrieb:
Threads wurden lediglich deswegen in die Diskussion miteingebunden gebracht um am Argument "man muss nicht immer den Index prüfen" zu rütteln.
ein argument, das ich nicht verstehe. wer um alles in der welt plant denn ernsthaft so ein programm, das zufällig oft funktioniert aber garantiert nicht immer funktioniert, und führt diesen wirklich schlechten stil (nichtsynchronisierte interthreadkommunikation) dann als argument auf, um einen anderen schlechten stil (exceptions gegen programmierfehler) zu verteidigen? womit habe ich das verdient?