Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)"
-
@Th69
Vielen Dank Th69.
Mir war es leider nicht klar. Schlimm!Der Iterator wird lokal im Thread deklariert.
Und dann wird die "map" dort damit durchlaufen.
Und ich dachte immer, dass lokale Deklarationen im Thread-eigenen Stack sind und bei Ende weggeschmissen werden.static VOID CClientThread(LPVOID lpvParam) { CString key; CClientThreadCtrl* p = (CClientThreadCtrl *)lpvParam; std::map<CString,DWORD>::iterator it; ... key = ...; it = p->m_pmapDaten->find(key); // entweder so for(it=p->m_pmapDaten->begin; ...){ // oder so, oder beides ... } ... }
-
@elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":
Und ich dachte immer, dass lokale Deklarationen im Thread-eigenen Stack sind und bei Ende weggeschmissen werden.
Der Iterator wird auch ordentlich weggeräumt. Die Frage ist: Was passiert mit dem Element, auf das der Iterator verweist. Wenn das Element gelöscht wird (z. B. in einem anderen Thread), dann kannst du den Iterator nicht mehr verwenden.
-
@Schlangenmensch
Ja, ich weiss, dass dann der eine Iterator kein Element mehr besitzt.
Diesen Fehler hatte ich ja auch, logischerweise, kann und habe ich aber auch abfangen.Der ganze Mist passiert ja auch ohne Element in der "map"!
Es genügt, dass ich auch einen "find()" auf die leere "map" ausführe und damit
diesem Iterator einen Wert, abhängig von der "map", zuweise.
Und dieser Iterator ist lokal in diesem Thread deklariert.Und andererseits kann ich aber im Thread ein Element in die "map" einfügen,
ohne dass es irgendwo Probleme gibt.
-
@elmut19 Du verwirrst mich. Wenn du
find()
aufrufst und kein Element findest, gibt es den "end()" iterator (zeigt auf ein "Element" hinter de Map).
Der darf nicht dereferenziert werden.Aber, du scheinst das Problem ja ganz gut eingekreist zu haben. Vielleicht kannst du damit jetzt ein minimal Beispiel liefern, an dem sich das Problem demonstrieren lässt.
-
@elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":
Diesen Fehler hatte ich ja auch, logischerweise, kann und habe ich aber auch abfangen.
Wie?
-
@elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":
Und dieser Iterator ist lokal in diesem Thread deklariert.
Trotzdem greift der Iterator auf Daten außerhalb des Threads zu.
static VOID CClientThread(LPVOID lpvParam) { CString key; CClientThreadCtrl* p = (CClientThreadCtrl *)lpvParam; std::map<CString,DWORD>::iterator it; ... key = ...; it = p->m_pmapDaten->find(key); // Stell dir hier mal die Frage was alles passieren kann. Annahme der Thread wird // hier unterbrochen. Dann könnte doch ein anderer Thread hier m_pmapDaten->clear() // aufrufen oder p->m_pmapDaten auf nullptr setzen, .... Sleep(2000); // Provokationstest: Tritt der Fehler nun öfters auf? for(it=p->m_pmapDaten->begin; ...){ ... } ... }
-
@wob 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)":
Diesen Fehler hatte ich ja auch, logischerweise, kann und habe ich aber auch abfangen.
Wie?
Ich habe CriticalSections drum rum gebaut und frage den Iterator zusätzlich auf Gültigkeit ab.
Wenn ich sonst auf ihn zugreife, zeigt er auf "end()".
-
@Schlangenmensch sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":
@elmut19 Du verwirrst mich. Wenn du
find()
aufrufst und kein Element findest, gibt es den "end()" iterator (zeigt auf ein "Element" hinter de Map).
Der darf nicht dereferenziert werden.Aber, du scheinst das Problem ja ganz gut eingekreist zu haben. Vielleicht kannst du damit jetzt ein minimal Beispiel liefern, an dem sich das Problem demonstrieren lässt.
Ja, zeigt dann einfach nur auf "end()".
Aber das genügt schon, dass der Fehler dann an anderer Stelle trotzdem auftritt.
-
@elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":
frage den Iterator zusätzlich auf Gültigkeit ab.
Wie das?
-
@elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":
und frage den Iterator zusätzlich auf Gültigkeit ab.
Wie machst du das? Wie kann man denn feststellen, ob ein Iterator gültig ist? Wenn genau das Element, auf das der Iterator zeigt, in einem anderen Thread gelöscht wurde zum Beispiel.
-
@Quiche-Lorraine 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)":
it = p->m_pmapDaten->find(key);
// Stell dir hier mal die Frage was alles passieren kann. Annahme der Thread wird
// hier unterbrochen. Dann könnte doch ein anderer Thread hier m_pmapDaten->clear()
// aufrufen oder p->m_pmapDaten auf nullptr setzen, ....Sleep(2000); // Provokationstest: Tritt der Fehler nun öfters auf?
for(it=p->m_pmapDaten->begin; ...){
...
}Ja!
Diese Probleme hatte ich auch. Aber die erkenne ich recht leicht und kann sie auch irgendwie verhindern.
Auch führt das zu einem anderen Fehler, nicht zu "_Orphan.."Für meine Tests habe ich ja auch extra immer andere Threads abgeklemmt, so dass nur einer läuft.
-
Zeige doch mal den (bezogen auf den Iteratorzugriff) relevanten Code der gesamten Funktion.
Da du keinenconst_iterator
verwendest, nehme ich mal an, daß Änderungen darüber an der Map durchgeführt werden, oder?
-
@Th69
Ja, genau das war der relevante Code, den ich oben angegeben habe.
Alles andere habe ich auskommentiert, auch das mit der "for"-Schleife.Parallel (also abwechselnd mit dem "find()") mache ich ein "insert()":
p->m_pmapDaten->insert(std::make_pair(csOwnMapKey, &p->m_pDaten));
Damit gab es keine Probleme.
In der kompletten Funktionalität werden "m_pDaten" natürlich auch manipuliert und wenn der Thread beendet wird, wird das "map"-Element auch gelöscht.
Ich habe meinen Code sogar soweit manipuliert, dass ich als "map" nur noch:
std::map<CString,DWORD> m_mapDaten; // wird als Referenz in den Thread durchgereicht
habe. Trotzdem passiert das mit dem "_Orphan..".
-
Sorry, aber ich gebe jetzt hier auf - so kann man dir nicht helfen.
-
@Th69 Trotzdem vielen Dank Th69
-
@elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":
Diese Probleme hatte ich auch. Aber die erkenne ich recht leicht und kann sie auch irgendwie verhindern.
Sorry, aber das glaube ich nicht. Dafür sind Threading Probleme zu hinterhältig, hartnäckig und gemein. Da musst damit rechnen, dass an jede Stelle dein Thread unterbrochen werden kann. Und neulich wurde ich auf einem 8-Bit Controller bei einem return durch einen Interrupt unterbrochen.
Ich vermute mal deine map wird von mehreren Threads gleichzeitig benutzt. Deswegen würde die map in eine Klasse kapseln. Als Vorlage könnte folgendes dienen:
class MyThreadSafeMap { private: std::map<CString,DWORD> mData; // oder wie auch immer std::mutex mMutex; // Wer hier Non-Ownership Pointer bzw. Referenzen nutzt wird // demnächst die Ackermann Funktion A(10, 10) rechnen dürfen. public: void DoSomeStuff() { std::lock_guard<std::mutex> Lock{ mMutex }; } };
Mache die Schnittstelle so weit wie möglich kompatibel mit deinem Programm. C++ ist an dieser Stelle sehr mächtig. Und dann mit X Threads testen, testen und nochmals testen.
Und dann setze diese Klasse in dein Programm ein.
-
@Quiche-Lorraine Vielen Dank Quiche-Lorraine.
Den Zugriff auf ungültige Iteratoren wirklich verhindern, dürfte wirklich schwer werden.
Vielen Dank für das Beispiel.
Aber wenn der Fehler auftrat, war er leicht zu erkennen.Letztlich wird aber nur ein Thread damit konfrontiert werden, auf einen plötzlich leeren Iterator zuzugreifen.
Die anderen 0 bis 20, 30 ,.. Threads arbeiten nur auf ihren eigenen Daten.
Aber trotzdem... Habe bemerkt, dass es nicht einfach ist.
Aber diese Herausforderung kommt ja erst noch, wenn das andere funktioniert.
-
Inzwischen habe ich etwas herausgefunden.
Irgendwie durch Probieren und irgendwie auch zufällig.
Ich wollte den Thread auch nicht so einfach ohne Lösung dastehen lassen.Da die Software ja schon recht alt ist und, so wie ich auch, sich meine Vorgänger nicht mit jeder Thread-Funktion
ausführlich beschäftigt hatten und die Software, so wie sie war, auch funktioniert hatte, kamen da ein paar alte "C-Gewohnheiten" zum Einsatz.Beim Versuch, den Aufruf von "TerminateThread()" wegen Zeitüberschreitungen, zu minimieren,
ist mir der Einfluss dieser Funktion aufgefallen.Durch Rumprobieren musste ich dann als Ursache diese blöden "Orphan"-Fehlern, auch das "ExitThread()" ausmachen.
Wenn man dann bei Microsoft nachliest, erfährt man, dass beide Funktionen den Thread "abschiessen", ohne die Destruktoren zu durchlaufen.... Naja...
Ich hoffe, die Info hilft dem Einen oder Anderen vielleicht auch noch.
Und vielen Dank nochmals an alle.
Und schöne Feiertage.
-
Das einzige was dir wirklich Gewissheit verschafft, ist die Lösung, die @Quiche-Lorraine gepostet hat. Die Map private in einer Klasse verstecken und jeden Zugriff darauf schützen. Alles andere ist Kristallkugellesen und somit Unsinn.
-
@Quiche-Lorraine sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":
// Wer hier Non-Ownership Pointer bzw. Referenzen nutzt wird
// demnächst die Ackermann Funktion A(10, 10) rechnen dürfen.Haha, gerade erst gelesen. Tolle Drohung, die klaue ich mir bei Gelegenheit