Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)"
-
Hast du den Zugriff gegeneinander verriegelt (Critical sections, mutex)?
-
@DocShoe
ja, natürlich. Daher auch die CritSec als struct-Element.
Und noch andere Verriegelungen...
Das Problem ist ja, dass es crasht, obwohl die map schon leer ist.
... Also auch dann. Auch wenn ich Elemente vom Elternobjekt aus löschen will
und alle Threads, die drauf zugreifen würden schon beendet sind.
Also echtes Dilemma.Es crasht also spätestens dann, wenn die maps alle leer sind und das
Elternobjekt am Ende seines Destruktors ist.Ich hatte natürlich auch Zugriffskonflikte. Aber die sind dann erkennbar.
Z.B. wenn ich ein Objekt beendet hatte, das Elemente löscht,
aber ein Thread noch lief, der dann auf das Element zugreifen will.
Das lässt sich alles erkennen.
-
Um an die CriticalSection eines Elementes zu kommen musst du ja schon Zugriff auf die map haben, dann ist das schon zu spät. Zeig mal bitte den Code, wo du auf die beiden maps zugreifst.
-
@elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":
@DocShoe
ja, natürlich. Daher auch die CritSec als struct-Element.Wenn du pro Eintrag eine
CRITICAL_SECTION
hast, dann wird das nicht funktionieren. Da verwenden dann ja unterschiedliche Threads unterschiedlicheCRITICAL_SECTION
und können daher trotzdem wieder gleichzeitig auf die Map zugreifen. Du musst für alle Threads die auf die selbe Map zugreifen die selbeCRITICAL_SECTION
verwenden.
-
@hustbaer @DocShoe
Das mit dem Code ist schwierig. Brauche da etwas, um vielleicht etwas zu extrahieren.Zu den CritSects: Jeder Thread holt sich seinen eigenen Eintrag.
Die eine CritSec in der Struct ist dann nur für diese Element zuständig.
Ich habe auch noch weitere übergeordnete CritSec dazu.
Auch müsste dann das Problem verstärkt während des Betriebs auftauchen.
Aber das Problem ist ja das Beende meines Programms.
Und dass es sogar dann crasht, wenn ich die maps sowie die darin enthaltenen
Objekte vorschriftsmässig "deleted" habe.
-
@elmut19
Der Anfang der Threads, Client oder Server, folgt diesem Schema:
Hier, der ClientThread. Der greift natürlich auf alle map-Elemente zu.
Die ServerThreads nur auf ihre eigenen. Und dafür ist diese CritSec gedacht.
Das funktioniert auch.
Wie gesagt, keine Probleme während der Arbeit der Threads!static VOID CClientInterruptThreadT2Ng(LPVOID lpvParam) { CMrSocketClientThreadT2Ng* p = (CMrSocketClientThreadT2Ng *)lpvParam; PMRCANIDSRAM pR = &p->m_CanPrt; // Klasse des Protokolls, enth. 3 "pCRingspeicher"-Objekte der Anlage. CString csOwnMapKey = _T("0");// Der eigene Key ist immer "0", zur Unterscheidung von den angeschl. Clients. CFehlerMeldung sLastError; TCPIP_PTK_T2_NG_HEADER sHeader; CANMESSAGEBUF SendVersionsMsg,ReadVersionsMessage; std::map<CString,PMRCANIDSRAM>::iterator itThreadRsps; char*pWritedate; int r = 0; DWORD rMax = 0, dwLastError = 0, dwOPenNoOf = 0, dwWaitRead = 0; BOOL bErstStart = TRUE; _Start: //------------------------------------------------------------------------------------------------------ // Allgemeine Überprüfung, ob auf Thread über die "std::map" noch zugegriffen werden darf. p->m_CanPrt.bInUse = TRUE; // 07.07.22: identisch mit "itThreadRsps->second->bInUse" itThreadRsps = p->m_pmapSocketThreadRsps->find(csOwnMapKey); if (itThreadRsps != p->m_pmapSocketThreadRsps->end()) { goto _Ende;// Bestehenden Eintrag nicht überschreiben! } else {// Hier identifiziert csMapKey = "0" das eigene Anlage-Objekt dieser SW im Client-Thread. p->m_pmapSocketThreadRsps->insert(std::make_pair(csOwnMapKey, &p->m_CanPrt)); } //------------------------------------------------------------------------------------------------------ // HP, 29.06.22: Die ersten Schritte (Connect zur Anlage) müssen über eigenen Ringspeicher erfolgen. // Hierzu gehören "Öffnen des Kanals", "Variable initialisieren", "Protokoll abfragen" itThreadRsps = p->m_pmapSocketThreadRsps->find(csOwnMapKey); if (itThreadRsps != p->m_pmapSocketThreadRsps->end()) { InitializeCriticalSection(&itThreadRsps->second->m_csRspCriticalSection);// nur eigene CS } //------------------------------------------------------------------------------------------------------ ... Hier kommt einiges ohne Einsatz von CritSecs, wie Befehlspuffer und div. Initialisierungen. Dann erst die Thread-Schleife ... while(!p->m_sMrSocketThreadDataAndState.m_iThreadAktionSoll)// Solange SOCKETCLIENTAKTION_SOLL_RUN (=0) { _New: if (p->m_pmapSocketThreadRsps) { for (itThreadRsps = p->m_pmapSocketThreadRsps->begin(); itThreadRsps != p->m_pmapSocketThreadRsps->end(); itThreadRsps++) { if (TryEnterCriticalSection(&itThreadRsps->second->m_csRspCriticalSection)) {// 12.07.22: NEIN? ->gleich weiter zum nächsten map-Element (no Wait) int iRet = itThreadRsps->second->pWriteRsp->readlinesize();// solange was im Ringspeicher, dieses an Controller senden if (itThreadRsps->first != csOwnMapKey) {// 20.07.22: debug int iiNoLines = itThreadRsps->second->pWriteRsp->NoOfLines();// solange was im Ringspeicher, dieses an Controller senden // TRACE(_T("CClientInterruptThreadT2Ng: Check nach Befehlen von Nr %s, vor While(pWriteRsp->readlinesize()=%d), NoOf=%d\n"), itThreadRsps->first, iRet, iiNoLines); } ...
Und natürlich auch das Ende eines (dieses) Threads
_Ende: TRACE (_T("T2NG ---------------------- ExitThread(0)\n"));// 13.07.22: auch die eigene CritSect löschen! int iHelp; //EnterCriticalSection(p->m_pcsSocketThreadsEndCriticalSection); // 10.08.22: auskommentiert, da es mit anderem "close()" kollidiert. itThreadRsps = p->m_pmapSocketThreadRsps->find(csOwnMapKey); if (itThreadRsps != p->m_pmapSocketThreadRsps->end()) { itThreadRsps->second->bInUse = FALSE; // setzt damit auch pR->InUse auf FALSE. if (itThreadRsps->second->pErrorRsp) { itThreadRsps->second->pErrorRsp->deinit(); delete itThreadRsps->second->pErrorRsp; itThreadRsps->second->pErrorRsp = NULL; } if (itThreadRsps->second->pReadRsp) { itThreadRsps->second->pReadRsp->deinit(); delete itThreadRsps->second->pReadRsp; itThreadRsps->second->pReadRsp = NULL; } if (itThreadRsps->second->pWriteRsp) { itThreadRsps->second->pWriteRsp->deinit(); delete itThreadRsps->second->pWriteRsp; itThreadRsps->second->pWriteRsp = NULL; } DeleteCriticalSection(&itThreadRsps->second->m_csRspCriticalSection); itThreadRsps = p->m_pmapSocketThreadRsps->end(); TRACE(_T("T2NG ---------------------- before ExitThread(0): std::mapRsp->erase(Key)\n"));// 13.07.22: auch die eigene CritSect löschen! iHelp = p->m_pmapSocketThreadRsps->erase(csOwnMapKey);vorhanden }
-
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
- vor dem Zugriff auf die map den Zugriff verriegeln
- dir den iterator holen
- Sachen mit dem iterator tun, bis er nicht mehr gebraucht wird
- 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:- 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. - 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. - 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.
- Beim Löschen eines map-Elements von einem Ort aus,
-
@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?