Programm bleibt bei WaitForSingleObject hängen...
-
Hi liebe Community
!Ich habe in meinem Programm folgendes Problem:
Ich erstelle meine Threads nach dem in diesem Thread beschriebenen Prinzip (also das von 'Codefinder'). Wenn ich aber unter WM_DESTROY mit WaitForSingleObject auf das ende warte, hängt sich mein Programm immer auf (also bleibt dort 'stehen', man kann nichts mehr eingeben/machen). Habt ihr ne Idee woran das liegen kann ? Habe vorm Aufruf von Waitfors... auch diese Varialbe auf true gesetzt, sodass das Abbruchkriteriem des Threads auch erfüllt ist. In der Thread-Funktion prüfen ich diese auch regelmäßig. Komisch ist auch, dass das nur bei WM_DESTROY so ist. Mein Programm besitzt einen Button der diesen Thread nochmal startet (wenn er beendet ist), da funktioniert das perfekt mit WaitFor... allerdings, lasse ich den Thread da auch auslaufen, ändere also diese Threadvariable (von wegen true > Thread aktiv, false > inaktiv) erst gar nicht.Wär echt super nett, wenn ihr mir da weiterhelfen könntet! Ich danke schonmal allen Helfenden
.Viele Grüße
TimonPS: Der Thread aktualisiert Informationen, was tw. ziemlich lange dauert...
-
2 Möglichkeiten:
1. Der Thread wird wirklich nicht beendet. Das kannst Du wohl im Debugger am einfachsten prüfen.
2. Du versuchst an das Fenster, dass zerstört werden soll noch eine Nachricht zu senden. Die kommt aber nicht an, weil der Thread auf WaitForSingleObject steht. Du hast einen Deadlock.
Schau im Debugger wo der andere Thread steht, der beendet werden soll.
-
Hi, erstmal Vielen vielen Dank für Deine Antwort!!!
Das könnte zutreffen, also...
Ich glaube das dein 1. Punkt zutrifft und beim 2. bin ich mir nich ganz sicher:
Wie kann ich prüfen (MS Visual C++ 2005 Express Edition), ob mein Thread noch läuft, bzw. ob sich dieser in einem Deadlock befindet ?Habe gerade mit der Spy den Nachrichteneingang geprüft und deine Vermutung hat sich bestätigt... da kommen 2 WM_CLOSE-Messages an, die sofort an WM_DESTROY weitergereicht werden. Aber mein progg bleibt trotz dieser kleinen Modifikation hängen (nur zu testzwecken):
// ... case WM_DESTROY: static bool fCalled = false; if(!fCalled) { // hier hin den gesamten WM_DESTROY-Code verschoben fCalled = true; } return 0; // ...Achja: Im Fall 1: Wie kann ich denn den Thread dann beenden, bzw. Warum beendet er sich nicht ?
Nochmals vielen Dank!

-
@:Timon: :
Die ganze "volatile Geschichte" in dem Code vom CodeFinder ist IMHO ganz böse rumgepfuscht. Wenn dann sollte die "bool Variable" selbst volatile sein. Also auf beiden Seiten, der Lesenden und der Schreibenden! Noch besser wäre dann wenn dieses "bool" eben kein "bool" sondern ein DWORD wäre.Und gleich noch viel besser wäre wenn du mit dem volatile aufhörst, und stattdessen eine CRITICAL_SECTION verwendest um den Zugriff auf jene Daten zu synchronisieren die beide Threads verwenden, also z.B. das Killflag (fAbortProcess).
Das Ultimum wäre natürlich wenn du lernst wie man sicher multithreaded programmiert, bloss das sprengt den Rahmen eines Threads (Forums-Thread meine ich jetzt) ganz eindeutig.
Und: rausbekommen welche Threads noch laufen lässt sich ganz einfach indem du im Debugger auf "Pause" drückst, und dann im "Threads" Fenster guckst welche Threads noch da sind. Wenn du da drinnen auf einen Thread Doppelklickst dann hast du im "Callstack" Fenster auch den Callstack dieses Threads, kannst also gucken wo der genau steht. Und dann kannst du auch in diesem Thread "steppen", falls er nicht in einer Funktion "hängt" (die also nichtmehr zurückkehrt).
p.S.: was auf jeden Fall nicht gut gehen kann ist wenn der Worker-Thread mit dem Fenster-Thread mittels SendMessage kommuniziert.
p.p.S.: eine Möglichkeit die u.U. auch gut wäre:
// in der WndProc: switch(message) { //... case WM_CLOSE: // Killflag setzen //EnterCriticalSeciton(&sdThreadData.cs); sdThreadData.fAbortThread = true; //LeaveCriticalSeciton(&sdThreadData.cs); if(WaitForSingleObject(hThread, 0) == WAIT_OBJECT_0) { // thread ist beendet, die DefWindowProc wird sich darum kümmern WM_DESTROY zu schicken break; } else { // thread läuft noch - timer setzen der immer mal wieder guckt ob der thread dann immer noch läuft SetTimer(hWnd, 99, 100, 0); // hier könnte man dann z.B. alle child fenster disabled (siehe text unten) return 0; // DefWindowProc NICHT aufrufen, so lebt unser Fenster auch noch weiter } case WM_TIMER: if(wParam == 99) { // es ist unser "gucken ob thread noch läuft" timer if(WaitForSingleObject(hThread, 0) == WAIT_OBJECT_0) { // thread ist beendet -> WM_CLOSE wird das Fenster jetzt schliessen PostMessage(hWnd, WM_CLOSE, 0, 0); } return 0; } else break; case WM_NCDESTROY: // oder WM_DESTROY //... CloseHandle(hThread); hThread = 0; //... break; //... } //... return DefWindowProc(...); }Natürlich müsstest du um das Schema zu verwenden sicherstellen dass, nachdem man auf "Close" gedrückt hat, der User nixmehr mit dem Fenster machen kann was er zu dem Zeitpunkt (wo das Programm quasi nurmehr drauf wartet sich beenden zu können) nicht soll. Eine einfache Möglichkeit wäre u.U. alle Controls (Buttons etc.) in dem Fenster zu disablen. Das würdest du dann z.B. auch im WM_CLOSE Handler machen. z.B. einfach alle Child Fenster enumerieren und disablen.
Der Vorteil hier wäre dass noch sämtliche Messages verarbeitet werden können bis der Thread wirklich terminiert. Also dürfte der Thread dann auch z.B. mittels SendMessage mit dem Fenster kommunizieren. Sobald der "Fenster Thread" in einem WaitForSingleObject hängen könnte dürfte er das ja nichtmehr.
p.p.p.S.: In diesem meinem Posting fehlen sicher so an die 100 Kommas (wie in den meisten meiner Postings
)
-
hustbaer schrieb:
[...] Die ganze "volatile Geschichte" in dem Code vom CodeFinder ist IMHO ganz böse rumgepfuscht. [...]
*Shot down in flames*
... Wieso ?
-
CodeFinder schrieb:
hustbaer schrieb:
[...] Die ganze "volatile Geschichte" in dem Code vom CodeFinder ist IMHO ganz böse rumgepfuscht. [...]
*Shot down in flames*
... Wieso ?Einfach so

Ok, ernst. Weil volatile in C++ schonmal sowieso nicht gescheit definiert ist (z.B. ist es nun ne memory-barrier oder nicht?). Weil du bloss beim Schreiben auf volatile castest und beim Lesen nicht. Weil das alles einfach fürchterlich ist.
Aber das ist ja nicht das einzige grauenhafte an dem Code...
Die "static" Daten sind sehr sehr schauderhaft, und nen Thread in C++ mit _endthreadex() abzubrechen ist auch schauderhaft. "return 0" aus der thread function wäre dir wohl zu trivial gewesen oder wie?
-
hustbaer schrieb:
CodeFinder schrieb:
hustbaer schrieb:
[...] Die ganze "volatile Geschichte" in dem Code vom CodeFinder ist IMHO ganz böse rumgepfuscht. [...]
*Shot down in flames*
... Wieso ?Einfach so

Ok, ernst. Weil volatile in C++ schonmal sowieso nicht gescheit definiert ist (z.B. ist es nun ne memory-barrier oder nicht?). Weil du bloss beim Schreiben auf volatile castest und beim Lesen nicht. Weil das alles einfach fürchterlich ist.
Deine Erklärung find ich n bissl lari fari^^ ...Hm...na volatile unterdrückt Compileroptimierungen (siehe: http://www.cppreference.com/keywords/all.html)
volatile
The volatile keyword is an implementation-dependent modifier, used when declaring variables, which prevents the compiler from optimizing those variables. Volatile should be used with variables whose value can change in unexpected ways (i.e. through an interrupt), which could conflict with optimizations that the compiler might perform.hustbaer schrieb:
Aber das ist ja nicht das einzige grauenhafte an dem Code...
Die "static" Daten sind sehr sehr schauderhaft, und nen Thread in C++ mit _endthreadex() abzubrechen ist auch schauderhaft. "return 0" aus der thread function wäre dir wohl zu trivial gewesen oder wie?
Wieso sind static-Daten schauderhaft ? Wie würdest Du denn die Daten definieren ? Global ? Oha...
Das mit dem _endthreadex() ist gewollt...man _sollte_ Threads mit _endthreadex() beenden, das return (0) danach ist also nur obligatorisch
.
Siehe:Terminating a thread with a call to endthread or _endthreadex helps ensure proper recovery of resources allocated for the thread.
Quelle: http://msdn2.microsoft.com/en-us/library/hw264s73(VS.80).aspx
-
man _sollte_ Threads mit _endthreadex() beenden
NIE!!!
-
b schrieb:
man _sollte_ Threads mit _endthreadex() beenden
NIE!!!
Also manchmal gehen mir diese 'Non-Sinns-Aussagen' von ungereggten Trolls ja doch extrem auf n S***
.Kannst Du das auch schlüssig begründen/belegen ?
-
_endthreadex is called automatically
-
_endthreadex helps ensure proper
Was meinst Du warum schreiben die das ?
-
Um dich zu verwirren.
-
Naja ich bleib bei meiner Meinung.
Das war auch mein letzter Post diesbezüglich.
-
@CodeFinder:
Blödsinn ^3.
Sorry, aber ich weiss wovon ich rede.#1:
MSDN schrieb:
You can call _endthread or _endthreadex explicitly to terminate a thread; however, _endthread or _endthreadex is called automatically when the thread returns from the routine passed as a parameter to _beginthread or _beginthreadex.
Und nu denk mal drüber nach was in C++ anders ist als in C:
#2:unsigned __stdcall ThreadFn(void*) { std::string foo = "012345678901234567890123456789"; _endthreadex(0); return 0; // braucht ein "guter" compiler hier garnicht }Und jetzt beobachte dein Memory-Leak. Holladeroh. Glaubst du der C++ Compiler kann zaubern und ruft alle Destruktoren von auto-Variablen auf wenn du das so machst? Nööööööhöhö.
_endthreadex ist unnötig und böse.Und volatile ist murks, es sei denn man weiss genau was man tut.
-
Hm Du meinst, das durch den Aufruf von _endthreadex() der Aufruf des Destuktor's (Klasse ist ja rille) verhindert wird
?
-
Hab diese Sachen mit volatile (und auch endthread) außem Petzold. Dachte das wär noch gültig
.
-
CodeFinder schrieb:
Hab diese Sachen mit volatile (und auch endthread) außem Petzold. Dachte das wär noch gültig
.Nur um Dich zu erinnern: Petzold ist C und nicht C++! Von Destruktoren war da nie die Rede. Sofern hat er einfach entsprechend vor dem Aufruf aufräumen müssen. Ein C++ Programmierer wird aber oft genug nicht weit genug denken was für Konsequenzen eine Funktion wie _endthreadex hat!
_endthreadex ist wirklich zu vermeiden!
Die beste Methode ist immer noch einfach aus der Thread Funktion zu returnieren und die CRT oder das OS den Rest machen zu lassen!Ich kannmich das hustbaer nur anschliessen!
-
Jops ok, man lernt nie aus
. Schankedön
.
-
CodeFinder schrieb:
Hm Du meinst, das durch den Aufruf von _endthreadex() der Aufruf des Destuktor's (Klasse ist ja rille) verhindert wird
?Ich meine es, und ich hab es sogar ausprobiert bevor ich hier gepostet habe

Kannst ja einfach mal ne Klasse zum probieren machen:class foo { public: foo() { std::cout << "foo ctor called." << std::endl; } ~foo() { std::cout << "foo dtor called." << std::endl; } };Wenn du dann in der ThreadFn eine lokale Variable vom Typ foo anlegst siehst du dass bei Verwendeung von _endthreadex() der ctor aufgerufen wird, der dtor allerdings nicht. Wenn du mittels return rausgehst werden beide aufgerufen.
Ausserdem machst du mit dem Aufruf von _endthreadex() die Funktion von _beginthreadex abhängig, d.h. die funktioniert dann in keinem Framework mehr welches vielleicht selbst noch cleanup Code ausführen will, oder auch nicht in Programmen die CreateThread oder _beginthread (ohne ex) verwenden.
In C++ sollte man einen Thread oder auch das Programm immer nur dadurch verlassen/beenden dass man aus der Thread Function bzw. main mittels return zurückkehrt. "exit(0)" ist in C++ auch "böse".
-
Jo danke auch nochmal @hustbaer!
Allerdings nutze ich CreateThread (bzw. _beginthread) sowieso nicht, wegen der CRT-Leaks. Aber hast natürlich Recht mit Deinem Einwand
.PS: Hatte ich in einem früheren Post gefragt, _wo_ Du Deine 'thread-bezogenen' (id est: Infos für den Thread selbst, Kontrollvariablen und Sync-Objekte, welches ja idr. alles in einem struct zusammengefasst wird) Daten definierst, da Du -so kam das rüber- etwas gegen statische Variablen hast^^.
PPS: Muss man den Zugriff auf die 'Thread-Datenstruktur' auch syncronisieren ? Ist das nicht eigentlich überflüssig ?
-
Äh.
Ich verwende niemals static Daten wenn es sich vermeiden lässt.
Ich programmiere schliesslich auch meistens "objektorientiert" (ein Unwort, weil es viel zu viele Meinungen darüber gibt wie OOP nun auszusehen hat, aber egal).Wenn ich ein Objekt habe welches einen eigenen Thread rausstartet, dann stehen die gemeinsam genutzten Daten natürlich als non-static Member in diesem Objekt. Der Thread bekommt einen Zeiger auf das Objekt mit, fertig.
Daten die bloss der Thread alleine braucht lege ich entweder im Thread mit new an (wenns gross ist), oder sie wandern einfach auf dessen Stack, wofür hat man den schliesslich.Wenn ein Thread detached läuft dann bekommt er meistens einen Zeiger auf die Daten die er braucht mit, wobei ich dann die Ownership an den Thread übergebe, d.h. der muss sich darum kümmern das "Datenobjekt" mittels delete wieder wegzumachen.
Und was das Zugriffe Synchronisieren angeht: du musst Zugriffe auf sämtliche Daten synchronisieren die von mehreren Threads verwendet werden. Einzige Ausnahme: wenn *ALLE* Threads nur lesend darauf zugreifen. Also z.B. Lookup-Tabellen die du einmal mit Werten befüllst (bevor du andere Threads rausstartest), und danach NURMEHR liest. Sobald aber irgendwer schreibt musst du synchronisieren.
Daten die ausschliesslich ein Thread verwendet sind natürlich nicht betroffen, wobei natürlich auch egal ist wenn das nicht der "Hauptthread" ist.