Multithreading: Msgs zwischen Threads



  • Hallo,

    ich hoffe ich bin im richtigen unterforum 🙂
    Ich hätte eine Frage zu Multithreading:

    Im moment verwende ich Gcc unter Windows und habe ein wenig mit createThread der winapi rumprobiert. Ist es dabei (einfach) möglich, messages zwischen verschiedenen, laufenden Threads zu versenden?
    Laut der Api sollte das ja mit postthreadmessage() funktionieren, aber da hängt sich bei mir einfach mein IDE auf (Omnet++) ohne eine fehlermeldung anzuzeigen.

    Ich müsste folgendes Szenario umsetzen:

    Klasse A wird 5 mal instanziert und erstellt je einen Thread mit einer Funktion in Klasse A.

    In jedem Thread der Instanz der Klasse A wird ein weiterer Thread erstellt, wessen Funktion in Klasse B ist.

    In bestimmten Fällen soll die Funktion in Klasse B dann eine message an den Thread in Klasse A schicken, um dort darauf reagieren zu können

    ist das mit der windows api möglich? Ich finde einfach keine Beispiele dazu.
    Oder muss ich auf externe libs umsteigen?

    Vielen Dank und Beste Grüße,
    Severin



  • 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? Ein einfaches, kleines Eventsystem ist nun nicht gerade schwer oder umstaendlich. (Ich persoenlich wuerde Multithreading nicht so nutzen, eher in der Richtung ... in Java werden sie Callable oder Runable genannt.)



  • Welche Vorteile hätte denn ein solches Eventsystem gegenüber meinem Ansatz?
    Wichtig ist auf jeden Fall, dass Klassen A und B weiterarbeiten, auch wenn Klasse A auf eine nachricht von B wartet (ich dachte bisher, das ginge nur mit Threads)



  • Nein, viele Wege fuehren nach Rom. Ich sage ja auch nicht, das du den Threadansatz aufgeben sollst, nur das du die Kommunikation zwischen den Threads selbst in die Hand nehmen sollst.



  • Frage/suche doch im WinAPI Forum danach.

    siehe auch: http://www.henkessoft.de/C++/MFC/mfc_einsteigerbuch_kapitel17.htm



  • 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 Klasse

    Was 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.


Anmelden zum Antworten