Datenverlust durch Cast bei Threading

Hallo,

ich hab mal wieder ein Problem:

Bei einer Threaderzeugung gehen mir Daten verloren, die… nicht sollten. :smile:
Ich habe dazu ein Objekt MyThreads, das meine Threads verwalten soll.
Mit MyThreads::createNewThread kann man so einen neuen Thread erzeugen.
Da der Windowsaufruf jedoch eine globale Funktion braucht, wird eine aufgerufen, die sofort wieder eine Objektfunktion aufruft.
Die Daten, die dabei immer übergeben werden sollen, sind jedoch weg. o_O

Hier mal ein bisschen Code zur Illustration:

/\* -- Eine einfache Datenstruktur -------------------- \*/
struct Data { int zahl, char buchstabe };

/\* -- MyThreads.cpp - Header lass ich mal weg :wink: ----- \*/

// Erzeugt einen neuen Thread, der das globale startThread aufruft
void MyThreads::createNewThread(Data\* dp)
{
 CreateThread(NULL, 0, startThread,
 (LPVOID)dp, 0, &newThread.dword);
}

// Globale Funktion, die von LPVOID wieder auf Data\*
// castet und die eigtl. Thread-Funktion aufruf
void startThread(LPVOID datap)
{
 MyThreads\* mtRef = MyThreads::instance(); // Singleton
 Data\* dp = (Data\*)datap;

 mtRef-\>runThread(dp); 

 return((DWORD)datap);
}

// Und schlussendlich die Funktion, in dem der Thread wirklich
// arbeitet
void MyThreads::runThread(Data\* dp)
{ 
 while(true)
 {
 // ... Arbeite ...
 }
}

So. Das Problem ist jetzt, dass beim Casten von LPVOID nach Data* die eigentlichen Strukturdaten verloren gehen. Soll heissen, ich kann mir folgende Ausgabe erzeugen lassen:

(createNewThread) Adresse: 0x01234567, Data.zahl = 1
(startThread) Adresse: 0x01234567, Data.zahl = 1838477
(runThread) Adresse: 0x01234567, Data.zahl = 1838477

Also zeigt der Pointer zwar immer auf die gleiche Stelle, aber der Speicherbereich scheint beim CreateThread-Aufruf trotzdem freigegeben worden zu sein.

Kann man das irgendwie umgehen? Ich kenne nämlich keinen anderen Weg, einem Thread irgendwelche Daten mitzugeben…

kvida

Hallo!

Ich würde hier keine Globale Funktion nehmen sondern die Klassenfunktion static machen.

Vermutlich gibst du den Speicher frei nachdem du MyThreads::createNewThread(Data* dp) aufgerufen hast.
Je nach Laufzeitverhalten kann es sein, dass du noch ein paar Zyklen (Stichwort: Quantum) lang im neuen Thread auf die Daten zugreifen kannst, ehe der erzeugende Thread die Daten freigibt.

Gruß Pauli!

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo Pauli!

Vermutlich gibst du den Speicher frei nachdem du
MyThreads::createNewThread(Data* dp) aufgerufen hast.
Je nach Laufzeitverhalten kann es sein, dass du noch ein paar
Zyklen (Stichwort: Quantum) lang im neuen Thread auf die Daten
zugreifen kannst, ehe der erzeugende Thread die Daten
freigibt.

Ja, das wars tatsächlich. Ich hatte nicht bedacht, dass nach Aufruf von MyThreads::createNewThread() der Hauptthread ja noch zurückkehrt (in diesem Fall in eine MyThreads::registerNewClient()-Funktion), wo er dann beendete und die Data-Struktur, die dort (nicht dynamisch) erzeugt wurden, out of scope ging. (-_-)"

Ich hab jetzt die Data einfach dynamisch allokiert, dann bleiben die auch bestehen (muss nur halt auf Memory Leaks achten, richtig?) und es geht.

Ich würde hier keine Globale Funktion nehmen sondern die
Klassenfunktion static machen.

Grrr… wieso haben immer alle Anderen die kürzere UND elegantere Designlösung? :wink:

Vielen Dank nochmal!

kvida

Dwer Zeiger dp wird auf
dem Stack aufgebaut.
Damit wird der Thread gestartet,
Der Aufrufer wird beendet und
dp wird ‚vernichtet‘.
Der laufende Thread
bekommt einen ganz anderen
undefinierten Wert
und es knallt.

Data* dp = (Data*)datap;

mtRef->runThread(dp);

return((DWORD)datap);

Man kann nun im Thread
selbst dp kopieren
und eine Warteanweisung
hinter dem Aufruf einbauen.
Ein par Millisekunden sollten reichen.
Je mehr, umso besser.
Das eigentlich referenzierte Objekt sollte aber
global sein.

Data* dp = (Data*)datap;

mtRef->runThread(dp);
Sleep(100);
return((DWORD)datap);

…

void MyThreads::runThread(Data* dp2)
{
Data *dp=dp2;
while(true)
{
// … Arbeite …
}
}