Multithreading: Msgs zwischen Threads
-
knivil schrieb:
ist das mit der windows api möglich? Ich finde einfach keine Beispiele dazu. Oder muss ich auf externe libs umsteigen?
Warum soll das fuer die WinApi machen oder irgendeine andere Lib? Warum nicht selbst machen?
Weil man es selbst nicht korrekt hinbekommt, wenn man auf einem Level ist, wo man solche Fragen stellt.
Aber natürlich: wieso was fertiges, funktionierendes verwenden, wenn man es selbst schreiben, und dabei Fehler machen kann.(OK, grundsätzlich ist gegen "unnötig" Fehler machen nichts einzuwenden. Wenn es Fehler sind, die man auch findet, den Fehler versteht, eine Lösung findet - die man auch versteht, und so daraus lernt. Beim Thema Multithreading ist es aber so dass man die Fehler meist nicht findet, und selbst wenn, nicht versteht, und dadurch nichts daraus lernt. Zumindest gilt das für die meisten Programmierer. Wer behauptet dass Multithreading nicht schwer ist, hat es entweder selbst nicht verstanden, oder ist einfach sehr schlau/sehr erfahren)
-
Durch ein sauberes vorher erstelltes Konzept lassen sich aber auch beim Multithreading Fehler vermeiden. Wenn sie jedoch auftreten, sind sie mitunter schwer zu finden

Edit:
ich hab noch ein Projekt rumliegen, was ich abgebrochen habe, weil ich die Ursache für den SegFault nie gefunden habe...
-
It0101 schrieb:
Durch ein sauberes vorher erstelltes Konzept lassen sich aber auch beim Multithreading Fehler vermeiden
Wie soll er ein sauberes Konzept erstellen, wenn er die Spielregeln nicht kennt?
BTW: kennst du sie? Bist du sicher? Ganz sicher?
Ich habe jedenfalls wesentlich mehr falschen Multithreading-Code gesehen, als richtigen.
-
hustbaer schrieb:
Ich habe jedenfalls wesentlich mehr falschen Multithreading-Code gesehen, als richtigen.
Und ich hab mehr falschen als richtigen Multithreading-Code produziert

Das ist nunmal einfach so.
Was ich damit sagen wollte, ist: Wenn man sich vorher vor Augen führt, wer mit wem kommuniziert, welche Kommunikation notwendig / sinnvoll ist und welche Kommunikation man auch irgendwie anders umgehen kann, dann kann das die Menge der auftretenden Probleme verringern. Nicht mehr wollte ich damit sagen

-
BTW: kennst du sie? Bist du sicher? Ganz sicher?
Ich hab nie irgendwelche niedergeschrieben "Spielregeln" gelesen. Aber da ich beruflich relativ häufig mit multithreaded-Serverentwicklung zu tun habe, habe ich inzwischen aus anfänglichen (zahlreichen) Fehlern viel gelernt und meine eigenen Erkenntnisse gesammelt

Und daher weiß ich auch, dass ein gutes Konzept hilfreich ist.
-
hustbaer schrieb:
Weil man es selbst nicht korrekt hinbekommt, wenn man auf einem Level ist, wo man solche Fragen stellt.
Das ist mein erstes C++ Projekt. Ich hab sonst nur Java erfahrung. Allerdings auch nicht mit Multithreading.
Wenn ich dem 2. Thread die ID des ersten Thread übergebe, könnte ich ja postThreadMessage() verwenden, oder ?
Ich müsste nur ein Struct erstellen, welches die Thread ID und einen anderes Objekt enthält, da ich dem 2. Thread noch was andres übergeben muss.
-
SeverinK schrieb:
Wenn ich dem 2. Thread die ID des ersten Thread übergebe, könnte ich ja postThreadMessage() verwenden, oder ?
Ich müsste nur ein Struct erstellen, welches die Thread ID und einen anderes Objekt enthält, da ich dem 2. Thread noch was andres übergeben muss.Der Empfänger-Thread braucht auf jeden Fall eine Message-Queue, sonst wird PostThreadMessage() scheitern. Außerdem kannst du mit PostThreadMessage() keine lokalen Daten übergeben (z.B. den Pointer auf eine struct), da der Aufruf asynchron ist und eine lokale struct nicht mehr existiert, wenn die Message bearbeitet wird.
Wäre es nicht einfacher mit Event-Objekten zu arbeiten? Das fände ich "natürlicher", falls deine Threads nicht ohnehin Messages verarbeiten. Kuck mal nach CreateEvent() und WaitForSingleObject().
Stefan.
-
Hallo,
Danke für den Vorschlag, Stefan!Ich habe mich nun ein wenig eingelesen und hätte nun folgende Idee:
Thread A erstellt ein event und eine instanz von klasse B (die brauch ich um in klasse B non static zugreifen zu können).
Die beiden werden dann in ein struct gepackt, und der Funktion von Thread B übergeben (welcher in Thread A erstellt wird)
In Thread B wird dann der Algorithmus durchlaufen und immer wenn es notwendig ist, wird das Event gesetzt. Soweit ich das verstanden habe, kann das event beliebig oft mit SetEvent() getriggert werden, oder?
Thread A merkt das dann mit WaitForSingleObject() und kann eine funktion in Klasse B aufrufen, welche das Objekt, das in Klasse B gefüllt wurde, zurückgibt.
-
SeverinK schrieb:
Hallo,
Danke für den Vorschlag, Stefan!Ich habe mich nun ein wenig eingelesen und hätte nun folgende Idee:
Thread A erstellt ein event und eine instanz von klasse B (die brauch ich um in klasse B non static zugreifen zu können).
Die beiden werden dann in ein struct gepackt, und der Funktion von Thread B übergeben (welcher in Thread A erstellt wird)
In Thread B wird dann der Algorithmus durchlaufen und immer wenn es notwendig ist, wird das Event gesetzt. Soweit ich das verstanden habe, kann das event beliebig oft mit SetEvent() getriggert werden, oder?
Thread A merkt das dann mit WaitForSingleObject() und kann eine funktion in Klasse B aufrufen, welche das Objekt, das in Klasse B gefüllt wurde, zurückgibt.
Öhöm --- ich hab mir das jetzt dreimal durchgelesen und versteh's nicht. Kein Kommentar also

Stefan.
-
hmmm

Dann versuch ichs nochmal (hoffentlich besser verständlich)
Thread A erstellt eine Instanz von Klasse B und ein neues event.
Diese Instanz und das Event packe ich dann in ein Struct.dann erstell ich in Thread A einen neuen Thread und übergebe an die Threadfunction dieses Threads den pointer zu dem vorher definierten Struct.
in dem neuen Thread (die Funktion ist in Klasse
mach ich dann wieder ein
Struct aus dem übergebenen pointer und kann somit das event auslösen, oder ?Dadurch dass in der Thread Funktion in Klasse B dann das event ausgelöst wird, welches in Thread A erstellt wurde, kann ich in Thread A mit WaitForSinlgeObject() darauf reagieren und in diesem Fall eine Funktion in Klasse B aufrufen, welche ein Objekt zurückgibt, welches in Thread B initialisiert wurde.
-
Hallo,
Das ist jetz das struct, welches an die threadFunction übergeben werden soll
typedef struct threadObject{ ClassB* cB; HANDLE* event; };In Klasse A hab ich jetzt folgendes in einem bereits laufendem thread:
DWORD WINAPI ClassA::threadForReq(LPVOID data){ ClassA* cA= (ClassA*) data; ClassB cB; HANDLE event; event = CreateEvent(NULL, FALSE, FALSE, 0); threadObject to = {&cB, &event}; HANDLE secondThread; secondThread = (HANDLE) CreateThread(NULL,0, cB.threadFunction, (void*) &to, 0, NULL); DWORD temp = WaitForSingleObject(event, INFINITE); switch (temp) { case WAIT_OBJECT_0: { //doStuff } } return ((DWORD) data); }Und das ist die threadFunction in ClassB nur um zu testen ob das SetEvent funktioniert:
DWORD WINAPI ClassB::threadFunction(LPVOID data) { threadObject* to = (threadObject*) data; ClassB* cB= to->cB; HANDLE* event = to->event; Sleep(1000); //do some stuff SetEvent(event); return ((DWORD) data); }Aber er kommt nie bis zum case WAIT_OBJECT_0
In dem Beispiel [url] http://msdn.microsoft.com/en-us/library/ms686915(VS.85).aspx [/url] wird das aber genauso gemacht, nur halt innerhalb einer KlasseWas hab ich da jetzt übersehn ?

Vielen Dank im Voraus,
Severin
-
DWORD WINAPI ClassB::threadFunction(LPVOID data) { threadObject* to = (threadObject*) data; ClassB* cB= to->cB; HANDLE* event = to->event; Sleep(1000); //do some stuff // SetEvent(event); // falsch - lässt sich aber dummerweise compilieren, da man void** (==HANDLE*) nach void* (==HANDLE) konvertieren kann SetEvent(*event); // richtig // oder halt so: HANDLE event2 = *(to->event); SetEvent(event2); return ((DWORD) data); }
-
Aaaaaah, das wars.. vielen dank

Das event sollte ja nach einmal SetEvent wieder auf "false" sein, oder ?
irgendwie stürzt mein programm wahllos ab und es scheint so, als würde
WaitForSingleObject öfters getriggert, obwohl ich nicht mal ne schleife oder ähnliches gebaut habe.
-
SeverinK schrieb:
Das event sollte ja nach einmal SetEvent wieder auf "false" sein, oder ?
ein auto-reset-event geht auf non-signaled sobald ein thread aufgeweckt wurde. das musst nicht mit dem zeitpunkt zusammenstimmen wo SetEvent aufgerufen wurde, denn es muss ja gerade kein thread da sein der wartet.
und ein manual-reset event bleibt sowieso signaled.
irgendwie stürzt mein programm wahllos ab und es scheint so, als würde
WaitForSingleObject öfters getriggert, obwohl ich nicht mal ne schleife oder ähnliches gebaut habe.aha
-
SeverinK schrieb:
Aber er kommt nie bis zum case WAIT_OBJECT_0
Das sollte dir zu denken geben. Wie wär's mit einem Test auf WAIT_FAILED und Auswertung von GetLastError()? Fehlerbehandlung ist manchmal ne ganz gute Idee.
SeverinK schrieb:
irgendwie stürzt mein programm wahllos ab und es scheint so, als würde
WaitForSingleObject öfters getriggert, obwohl ich nicht mal ne schleife oder ähnliches gebaut habe."Stürzt wahllos ab" ist nicht wirklich eine erschöpfende Beschreibung. Kriegst du Meldungen? Falls ja, welche?
Und lebt das an den Thread übergebene Objekt wirklich lange genug? Immerhin legst du es in einer Funktion als lokales Objekt an! Mach mal eine globale Variable daraus - nur zum Testen - oder verwende new(). Wenns dann keine Abstürze mehr gibt (oder andere
) bist du vielleicht einen Schritt weiter.Stefan.
-
DStefan schrieb:
"Stürzt wahllos ab" ist nicht wirklich eine erschöpfende Beschreibung. Kriegst du Meldungen? Falls ja, welche?
Und lebt das an den Thread übergebene Objekt wirklich lange genug? Immerhin legst du es in einer Funktion als lokales Objekt an! Mach mal eine globale Variable daraus - nur zum Testen - oder verwende new(). Wenns dann keine Abstürze mehr gibt (oder andere
) bist du vielleicht einen Schritt weiter.Stefan.
"Stürzt wahllos ab" is leider das einzige was ich dazu sagen kann, weil sich mein IDE (omnet++) aufhängt und die meldung bekomme "Project.exe reagiert nicht mehr".
Auch im debug modus kann ich nicht mehr herausfinden.Wenn ich das projekt zum 1. mal starte läuft es durch. wenn ich es dann stoppe und gleich wieder starte hängt es sich auf.
-
Ich habe den Thread und das Event jetzt global gemacht, aber es ist immer noch wie zuvor:
Ich starte das programm das 1. mal und es läuft. Starte ich es danach gleich nochmal, hängt es sich einfach auf ohne eine Fehlermeldung oder sonstiges auszugeben.
Auch im debugger kann ich nicht eingrenzen wo genau der Fehler auftritt
-
Dann hilft dir vielleicht ein Trace weiter: Erstelle dir einen globalen std::ofstream auf eine Datei deiner Wahl. Schreibe an kritischen Stellen Trace-Einträge in den Stream. Wenn das Programm abgestürzt ist, solltest du zumindest herausfinden können, was es zuvor gemacht hat. Etwa so:
#include <fstream> std::ofstream trace("c:\\dev\\myprogram\\trace.txt"); void func() { trace << GetCurrentThreadId() << ": func() 0" << std::endl; bar(); trace << GetCurrentThreadId() << ": func() 1" << std::endl; DWORD rc = WaitForSingleObject(); trace << GetCurrentThreadId() << ": func() 2" << std::endl; // ... usw. trace << GetCurrentThreadId() << ": func() exit" << std::endl; }Indem du mit GetCurrentThreadId() immer die Thread-Id davor schreibst, weißt du, welcher Thread was gemacht hat.
Das Problem mit dem Trace ist natürlich, dass er möglicherweise das Programm so verlangsamt, dass die Fehler nicht mehr auftreten. Einen Versuch ist es aber vielleicht wert.
Stefan.
-
Danke für die Antwort!
bevor ich das mache, hab ich mal einfach einige zeilen auskommentiert und bin dadurch jetz darauf gekommen, dass das Thread erstellen manachmal einfach nicht funktioniert

Also das Thread erstellen läuft ja bereits in ner Threadfunktion eines anderen Threads.
Dieser erste Thread wird noch ohne probleme erstellt, und nur wenn ich
HANDLE secondThread; secondThread = (HANDLE) CreateThread(NULL,0, cB.threadFunction, (void*) &to, 0, NULL);auskommentiere läuft er auch durch. Lass ich dieses CreateThread stehen, kommt es zu dem Fehler (wie gesagt, nicht immer..) Auch wenn ich in der threadFunction dieses secondThreads gar nichts mache, den Thread also nur erstelle, kommt es zu dem Fehler. Das selbe, wenn ich als Parameter nicht das struct sondern gar nichts übergebe.
Ich brauche aber den zweiten Thread in dem ersten Thread, sonst kann ich nicht umsetzen, was ich geplant habe.
Vielen Dank für die Hilfe,
Severin