Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)"



  • Jau, da haben wir doch schon das Problem.
    In Zeile 23 greifst du ohne Verriegelung auf die map zu, das könnte der Grund für den Fehler sein.

    Du musst in beiden Threads

    1. vor dem Zugriff auf die map den Zugriff verriegeln
    2. dir den iterator holen
    3. Sachen mit dem iterator tun, bis er nicht mehr gebraucht wird
    4. die Verriegelung wieder aufheben


  • Gibt es bei der Anwendung einen Performancehintergrund? Sonst könnte man ja prinzipiell auch eine CritSect um die ganze Map ziehen, anstatt um einzelne Einträge.

    Wobei meiner Erfahrung nach, zumindest bei unseren Projekten, die Performance erst dann stimmt, wenn man generell auf CriticalSections verzichtet und auch die Struktur der Multithreading-Anwendung komplett auf LockFree umstellt.



  • @DocShoe
    Das (Zeile 23, erster Codeteil) kann es eigentlich nicht sein.
    Es müsste dann ja an dieser Stelle krachen.
    Die map muss zwangsläufig gleichzeitig gelesen und bearbeitet werden,
    sonst funktioniert die ganze Sache nicht.
    Und der Datenaustausch über diese map funktioniert ja, mit mehreren Threads.

    Nur die einzelnen map-Elemente dürfen nicht gleichzeitig bearbeitet werden.

    Ich kann mir eine CritSec nicht als Grund für dieses (prinzipielle??) Problem vorstellen.



  • @It0101
    Hallo lt0101
    Performance brauchts immer, auch hier. Sehr wichtig.
    Aber die Absicherung durch CritSecs brauche ich auch.
    Habs während des Aufbaus auch ohne probiert.

    Selbst wenn es nicht crashen würde, könnte ich ohne nicht sicherstellen,
    dass auf eine Anfrage von client-Programm A an die Anlage auch die
    zugehörige Antwort genau an diesen Client zurück kommt.

    Eine Frage-Antwort Aktion muss immer komplett abgeschlossen sein,
    bevor man zur nächsten gehen kann. Und die ServerThreads, die das verteilen,
    müssen das eingephast sein.



  • @elmut19
    Ich habe mir angewöhnt, komplett auf CritSects zu verzichten, was auch funktioniert. Man arbeitet stattdessen mit Queues ( lock-free ) und versendet die Daten mittels Q an denjenigen, der sie braucht. Somit gibt es keine Zugriffe mehr auf den Datenhalter ( z.B. Storage ). D.h. jeder der die Daten braucht bekommt gewissermaßen eine Kopie, teilweise auch mit shared_ptr, wo das möglich ist. In meinem neuen Projekt bekommt jeder ClientThread alle Daten und sortiert selber aus, was er davon braucht. Dadurch gar kein Verwaltungsaufwand und null critsects. Funktioniert in meinem Anwendungsfall hervorragend und ist superschnell.

    Daher für meine Anwendungsfälle mein Fazit: CritSects sind der Tod für die Performance.



  • @DocShoe
    Es crasht aus folgenden Gründen:

    1. Beim Löschen eines map-Elements von einem Ort aus,
      der dieses Element nicht reingesetzt hat
      und zusätzlich der Thread schon beendet ist, auch wenn
      Objekte innerhalb dieses map-Elements ordnungsgemäss bereinigt wurden.
    2. Wenn ich versuche die ganze Anwendung zu beenden.
      Und da am Ende des Destruktors des Objekts, das diese maps als Member enthält
      und auch die ganzen Threads startet und beendet.
      Und das passiert obwohl ich dafür gesorgt habe, dass die maps alle leer sind.
    3. Es gibt noch diverse Zwischen-Möglichkeiten, die ebenfalls zum Absturz führen.

    Zu 1: Wenn ein Thread beendet wurde, so enthält das zugehörige map-Element
    ungültige Daten. egal ob diese vorher bereinigt und genullt wurden oder
    der Thread rücksichtslos gekillt wurde. Es sei denn der Thread hat sein
    map-Element selbst gelöscht.
    zu 2: Der Punkt ist für mich völlig unlogisch, nachdem ich es ja geschafft habe,
    die Elemente irgendwie zu löschen.



  • @It0101
    Dann wäre es der Ansatz, auf maps für den Datenaustausch gänzlich zu verzichten.
    Da weiss ich nicht, wie ich es realisieren kann.

    Wie man vielleicht erahnen kann, handelt es sich um eine Erweiterung einer bestehenden SW.
    Da war dieser Schritt der naheliegendste.

    Wie schon gesagt.
    Ich sehe nicht den Grund in den CritSecs, sondern in den maps selbst.
    Aber ich kenn den Grund nicht.



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    std::map<CString,PMRDATA> m_mapData;

    Mal eine Frage. Warum nutzt du hier PMRDATA und nicht MRDATA?



  • @It0101
    Es gibt natürlich für dies Art der Anwendung verschiedene Design-Ansätze.
    Man könnte anderen Client-Programmen über RPC den Zugriff auf gespeicherte
    Daten geben.
    Diese SW hat nun mal ein Design, das einen TCP-Thread verwendet, der mit
    der Verarbeitung der Daten einen schon sehr aufwendigen Aufbau hat.
    Hier war diese Variante noch die einfachste und logischste.

    Über Queues habe ich mir hier noch keine Gedanken gemacht, da eben im Hintergrund
    doch ein recht komplexer Aufbau mit Read- und Write-Puffer, etc. steckt, um damit
    die Asynchronität zwischen dem Bedienteil und der Kommunikation zu gewähren.
    Das alles lies sich recht einfach (naja...) in bestehendes Design integrieren.
    ...Einfacher als alle andere.



  • @Quiche-Lorraine
    Hallo Quiche-Lorraine,
    Tja, ich habe bereits beides versucht (PMRDATA und nicht MRDATA?).
    Und beides endete im selben Dilemma.

    Seit vier Wochen versuche ich alle möglichen Varianten,
    die mir bisher eingefallen sind, ausser, auf maps zu verzichten.

    Wäre der ganze Hintergrund einfacher zu beschreiben,
    und ich könnte mich auf ein paar Codezeilen beschränken,
    hätte ich schon früher im Forum nachgefragt.



  • Ich habe ganz oben in meiner Anfangs-Formulierung noch die Aufrufliste innerhalb der STL ergänzt.
    Die Tatsache, dass hier alles in einer "_Orphan_XXX" Function endet, sagt mir, dass die Software glaubt,
    dass hier "verwaiste" Objekte irgendwo rumliegen, obwohl das (zumindest meiner Meinung nach)
    und zumindest für einige Fälle nicht der Fall sein kann.



  • Lol
    Sorry, aber wenn du uns nix glaubst, wieso sollten wir dann weiter versuchen dir zu helfen.

    Das

    Das (Zeile 23, erster Codeteil) kann es eigentlich nicht sein.
    Es müsste dann ja an dieser Stelle krachen.
    ist halt einfach nicht wahr.

    Und das

    Die map muss zwangsläufig gleichzeitig gelesen und bearbeitet werden,
    sonst funktioniert die ganze Sache nicht.
    auch nicht. Du kannst vor jedem Zugriff locken, dann den Zugriff machen und dann unlocken. Das machen quasi alle C++ Programme so. Oder du begibst dich auf die Suche nach einer concurrent map für C++.

    So. Nu kannst du aus dem was dir geschrieben wurde was machen, oder du kannst es weiterhin ignorieren und behaupten dass das nicht der Grund sein kann weil blah. Mir wörst. Ich bin auf jeden Fall hier raus.



  • @hustbaer
    Auf jeden Fall vielen Dank Euch allen.
    Ich werd das mit den zusätzlichen locks versuchen.
    Auch wenn es mir seltsam erscheint. Verlieren kann ich ja nix dabei.
    Werde dann berichten.
    Was alles im Hintergrund dabei abgeht, in meiner Version, würde ich auch gerne verstehen.



  • Habe zur Sicherheit noch etwas nachgelesen, z.B. bei Stackoverflow zu "race conditions" "std::map"
    Natürlich kann ich mich immer noch irren.
    Aber ich bin weiterhin davon überzeugt, dass mein Hauptfehler nicht die CritSec sind.
    Den Schutz der Elemente in der "map" habe ich sehr wohl bedacht!
    Und wenn ich ohne Absicherung ein Element lösche, auf das ein Iterator noch zeigt, kommt anderer Fehler.
    "erase(key)" und "insert(..)" habe ich zudem auch auf "map"-Ebene abgesichert. Also schon Einiges ergänzt.
    Nun habe ich mein Programm noch so eingestellt, dass nur noch ein Thread läuft, der auch mit nur einer
    der "maps" arbeitet. Ich kann also von vornherein Zugriffskonflikte ausschliessen.

    Wenn ich das Objekt beenden will, dem die maps gehören, so stürzt es ab, und das obwohl die maps
    zuvor ordentlich gelöscht wurden (erase(key)). Gelöscht von dem Thread, der die Elemente gespeichert hat.

    Die maps enthalten, in der "struct", auf die sie als ihr Element verweisen, selbst komplexe Objekte,
    die allerdings zu dem Objekt gehören (also die komplette "struct"), das den TCP-Thread startet.
    Also zum Zeitpunkt, wo dieses Basis-Objekt an sein Ende kommt, existieren die Objekte, die die
    Threads starteten, sowie deren "structs" nicht mehr. Aber sie wurden auch vorher schon bereinigt.

    Also die "maps" enthalten als Element nur Verweise auf eine "struct".
    Und es kommt in diese "void _Orphan_ptr(const _Nodeptr _Ptr) noexcept" aus den STL-Methoden.

    Hat niemand eine Idee?



  • Dieser Beitrag wurde gelöscht!


  • Bin zu dem Schluss gekommen, dass sich eine "std::map" nicht mit einer "struct", die selbst komplexe Objekte enthält, verträgt.
    Auch wenn ich nur einen NULL-Pointer in meine "map" reinschreiben und vor Ende meines Basis-Objekts alles aus der "map" bereinigt wurde,
    gehts schief. Und der Thread, der als einziger mit dieser "map" gearbeitet hat, ist auch schon ordentlich beendet und hat dabei sein "map"-Element gelöscht.

    Ich werd wohl ne eigene "map" schreiben müssen.
    Danke aber nochmals für Eure Hilfe.



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Ich werd wohl ne eigene "map" schreiben müssen.

    So hab ich auch mal gedacht, das ist aber eine Sackgasse. Container kann man mal zu Lernzwecken implementieren, aber nicht zur ernsthaften Verwendung. Die STL-Container sind für ihren jeweiligen Anwendungsfall das beste was du bekommen kannst.
    Bei uns im Unternehmen gab es in der pre-C++11-Zeit auch viele selbstimplementierte Container, aber das war alles fürchterlich. Ich habe dann nach und nach nachgewiesen, dass die Performance der STL erheblich besser ist und die Usability auch. Seitdem kam keiner mehr auf die Idee sowas selber zu schreiben.

    Wenn die Map nicht zu deinem restlichen Konzept passt, dann ist vermutlich dein restliches Konzept ungeeignet und nicht die Map.

    Was das "struct" angeht, sehe ich das ähnlich. Das funktioniert zwar, aber ich vermeide das auch. "structs" sind bei mir entweder PODs oder befördere sie zu einer Klasse.



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Bin zu dem Schluss gekommen, dass sich eine "std::map" nicht mit einer "struct", die selbst komplexe Objekte enthält, verträgt.

    Da liegst du völlig falsch.

    Auch wenn ich nur einen NULL-Pointer in meine "map" reinschreiben und vor Ende meines Basis-Objekts alles aus der "map" bereinigt wurde,
    gehts schief. Und der Thread, der als einziger mit dieser "map" gearbeitet hat, ist auch schon ordentlich beendet und hat dabei sein "map"-Element gelöscht.

    Klingt als hättest du dir irgendwo den Speicher "versaut".

    Ich werd wohl ne eigene "map" schreiben müssen.

    Das halte ich wiederum für einen groben Feher.



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Ich werd wohl ne eigene "map" schreiben müssen.

    Da kann ich mich meinen Vorrednern nur anschließen, die STL oder vergleichbare boost-Container sind das Beste, was du bekommen kannst. Daran arbeiten Leute, die den ganzen Tag nichts anderes machen, was Selbstprogrammiertes wird an die Qualität wohl selten herankommen.
    Das Problem ist aber nicht die map selbst, sondern etwas anderes (die Vermutung race condition steht immer noch im Raum). Und das wirst du auch mit einer selbstprogrammierten Map nicht lösen können.

    Lass mal einen statischen Code Analyser über deinen Quelltext laufen (zB cppcheck), die finden oft Ungenauigkeiten/Fehler. Ansonsten führ´ dein Programm in einem Profiler aus, der Speicheranalysen durchführt (check auf double frees, Schreiben in nicht-initialisierten Speicher, etc.). Vielleicht hilft dir das weiter.



  • @It0101 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Ich werd wohl ne eigene "map" schreiben müssen.

    Bei uns im Unternehmen gab es in der pre-C++11-Zeit auch viele selbstimplementierte Container, aber das war alles fürchterlich. Ich habe dann nach und nach nachgewiesen, dass die Performance der STL erheblich besser ist und die Usability auch. Seitdem kam keiner mehr auf die Idee sowas selber zu schreiben.

    Da habe ich ein Deja-Vue. An Teil 1 arbeite ich noch (also Überzeugung, nicht eigene Container), aber den Mythos "std::vector ist langsam" und "std::vector" ist für viele Elemente nicht geeignet" hält sich hartnäckig :(.


Anmelden zum Antworten