Thread der endet aber nicht beendet ?
-
Martin Richter schrieb:
Ich glaube eher, dass die Ursache in der Verwednung von AfxBeginThread liegt.
Man muss mit m_bAutoDelete verhindern dass das Objekt zerstört wird und der Zeiger auf das CWinThread Objekt ungültig wird. Und mit dem Zeiger eben auch das Handle. Dies ist notwendig wnen man auf den Thread wartet.Er nutzt ja nichtmal die regulären Methoden, um auf den Thread zu warten.
@noname: Statt der Variablen solltest du eine vernünftige Synchronisation über Events und WaitFor... verwenden, dann sollte es auch funktionieren.
-
Dieser Thread wurde von Moderator/in Jochen Kalmbach aus dem Forum WinAPI in das Forum MFC (Visual C++) verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
Hallo,
also ich verwende natürlich diese einfache Zugriffsmethode, wie sie in meinen
Büchern beschrieben ist, das war das einfachste auf Anhieb:void foo() { threadcontrol.globalshutdown=true; while (threadcontrol.benchmarkstatus); }Ist es tatsächlich ein Problem, von mehreren Threads aus eine Variable abzufragen ?
Ich meine schreiben ist natürlich ein Problem, aber das blose abfragen ?
Windows arbeitet meiner Info nach noch immer nacheinander die Befehle ab, nicht parallel.
Ich speichere nach dem Aufruf mein CWinThread* Pointer natürlich. Auch verwende
ich im Moment deren ExitInstance() Funktion. Aber das erscheint mir zu brutal.Critical section habe ich hier im Forum irgendwo so herausgelesen:
CRITICAL_SECTION cs; UINT BenchMarkThread(LPVOID param) { threadcontrol.benchmarkstatus=true; while(threadcontrol.globalshutdown==false) { EnterCriticalSection(&cs); QueryPerformanceCounter(&benchmark.SP_PC_Start); Sleep(2000); QueryPerformanceCounter(&benchmark.SP_PC_Stopp); double PSec_PC=double(benchmark.SP_PC_Stopp.QuadPart-benchmark.SP_PC_Start.QuadPart)/double(benchmark.PC_Freq.QuadPart); LeaveCriticalSection(&cs); } threadcontrol.benchmarkstatus=false; return 1; }Ist da noch was falsch ?, denn es taugt nix.
Grüße
thenoname
-
Ist es tatsächlich ein Problem, von mehreren Threads aus eine Variable abzufragen ?
Nope, das wäre OK. Du tust aber was anderes. Du schreibst eine Variable in einem Thread, und fragst sie in einem anderen ab.
...
Und BenchMarkThread müsste so aussehen:
UINT BenchMarkThread(LPVOID param) { EnterCriticalSection(&cs); threadcontrol.benchmarkstatus=true; while(threadcontrol.globalshutdown==false) { QueryPerformanceCounter(&benchmark.SP_PC_Start); LeaveCriticalSection(&cs); Sleep(2000); EnterCriticalSection(&cs); QueryPerformanceCounter(&benchmark.SP_PC_Stopp); double PSec_PC=double(benchmark.SP_PC_Stopp.QuadPart-benchmark.SP_PC_Start.QuadPart)/double(benchmark.PC_Freq.QuadPart); } threadcontrol.benchmarkstatus=false; LeaveCriticalSection(&cs); return 1; }Erklärung: du musst die Zugriffe auf die gemeinsam verwendeten Variablen mit Enter-/LeaveCriticalSection "einklammern".
Stell dir einfach vor dass bei "EnterCriticalSection" alle Änderungen von die andere Threads im Hauptspeicher gemacht haben für den aktuellen Thread sichtbar werden (also alles neu "geladen" wird - man nenn das auch "acquire semantisc"), und dass bei "LeaveCriticalSection" alle Änderungen die der aktuelle Thread gemacht hat in den Speicher zurückgeschrieben werden ("release semantics").Um also eine Änderung die in Thread A passiert in Thread B sichtbar zu machen muss du in Thread A nach der Änderung LeaveCriticalSection aufrufen (release), und vor der Abfrage in Thread A musst du EnterCriticalSection aufrufen (acquire).
Wenn du sämtliche Zugriffe auf gemeinsam verwendete Daten mit Enter-/LeaveCriticalSection einklammerst regibt sich das automatisch.
Zusätzlich schützt es dein Programm davon dass 2 Threads gleichzeitig an den selben Daten rumändern, da immer nur ein Thread in der "critical section" sein kann, also in einem Bereich zwischen EnterCriticalSection(cs) und LeaveCriticalSection(cs) (bei gleichem cs).Der Grund warum deine Warteschleife nicht abbricht ist aber garnicht in BenchMarkThread zu finden, sondern in der Warteschleife

Die muss dann nämlich so aussehen:void foo() { EnterCriticalSection(&cs); // acquire threadcontrol.globalshutdown=true; // ändern LeaveCriticalSection(&cs); // release (Änderung "wegschreiben") while (1) { bool done; EnterCriticalSection(&cs); // acquire (Änderungen holen) done = !threadcontrol.benchmarkstatus; // Wert lesen LeaveCriticalSection(&cs); // release if (done) break; Sleep(1); // wenn hier kein Sleep steht wird der andere Thread unter Vista u.u. niemals die CRITICAL_SECTION bekommen... } }Natürlich geht es auch etwas einfacher, aber das Schema sollte klar sein hoffe ich.
----
Aber wie gesagt: wenn du bloss willst dass es geht dann schreib "volatile" vor die "globalshutdown" und "benchmarkstatus" Membervariablen, und der Thread wird terminieren.
-
Hi,
vielen Dank für deine Ausführungen, das hab ich leider nicht ganz aus den vielen
Threads hier im Forum herauslesen können. Die Critical_Section muss also, und das
ist mir jetzt erst bewusst geworden, bei allen Variablenzugriffen verwendet werden sein, die in Threads geshared werden.
Klar, wie sollte es sonst funktionieren.
Ich will eigentlich nicht "nur" das es geht, sondern ich will die für Windows
konformste Lösung erreichen. Anscheinend gibt es noch CCriticalSection die mit
lock und unlock arbeiten und weitere selbige Funktionen.Was mir allerdings noch komisch erscheint ist, das ich einen DebugPoint an return 1;
setzen kann und er dort niemals ankommt. D.h. er würde also wegen doppelten Zugriff
den Thread schlichtweg beenden ohne Fehlermeldung. Würde er wegen globalshutdown
die while Schleife verlassen und nur beim Setzen von
threadcontrol.benchmarkstatus=false;
abstürzen, müsste hier der der Debug Point funktionieren, bevor er die Variable
ungeschützt setzt, das ist aber auch nicht der Fall.Verlässt das Programm den Thread weil ich globalshutdown setze, während er gerade
bei while abfrägt, müsste es nur sporadisch nicht funktionieren.Grüße
thenonameps. ein Sleep(10) habe ich natürlich in der Warteschleife eingebaut, um nicht die Prozessorlast zu maximieren.
-
Wenn du schon immer ein Sleep(10) in der Warteschleife hattest, dann hätte es (trotz fehlender CRITICAL_SECTION) eigentlich schon immer funktionieren müssen. Nicht dass es "OK" wäre es deswegen so zu machen. Ist auch leider nicht in einem Satz zu erklären wieso es dann hätte funktionieren müssen

sondern ich will die für Windows konformste Lösung erreichen
Die "konformste" (und auch üblichste) Lösung wäre anstelle einer Warteschleife mit einem Flag einfach "WaitForSingleObject(threadHandle, INFINITE)" zu verwenden.
Das "Signal" an den Thread (das "Killflag", in deinem Fall heisst das Killfalg eben "threadcontrol.globalshutdown") wird oft als "volatile int" implementiert. Wie "sauber" das ist darüber kann man streiten, aber es funktioniert unter Windows mit MSVC und GCC bombensicher (sollte eigentlich sogar mit jedem Compiler gehen).
Die Critical_Section muss also, und das
ist mir jetzt erst bewusst geworden, bei allen Variablenzugriffen verwendet werden sein, die in Threads geshared werden.Jo. AUSSER eben wenn ALLE Threads NUR lesen. Den Fall hast du z.B. wenn du in der Phase wo erst 1 Thread läuft (der "Hauptthread") irgendwelche Tables initialisierst, und die danach immer nur liest aber nie schreibst. Solche Daten kannst du natürlich ohne jegliche Synchronisierung aus allen Threads gleichzeitig lesen. (Das ist deswegen OK weil in jeder mir bekannten threading-Library der CreateThread Aufruf "release" Semantik hat, und der Start der Ausführung des neuen Threads "acquire" Semantik hat. Dadurch ist sichergestellt dass ein neuer Thread alle Änderungen sehen muss die der erzeugende Thread vorher gemacht hat, und da danach nixmehr geändert wird, ist das so OK)
----
Wenn du nochmal den Code in der Thread-Funktion und den Code der das Killflag setzt herzeigst kann ich nochmal reingucken, vielleicht ist ja doch noch was anderes falsch.
Ansonsten kannst du statt Breakpoints auch einfach OutputDebugString oder TRACE (MFC) verwenden um eine Meldung in die Debug-Konsole zu schreiben, dann siehst du genau wann die Codestelle ausgeführt wird.
p.S.: wenn du AfxBeginThread verwendest bekommst du ein CWinThread Objekt zurück wo das m_bAutoDelete Member auf "true" gesetzt ist. Dadurch zerstört der Thread bevor er sich beendet das Objekt automatisch, und das Thread-Handle wird freigegeben. In dem Fall darf man WaitForSingleObject nicht verwenden, da das Handle ja freigegeben wird bevor der Thread terminiert, WaitForSingleObject allerdings erst zurückkehren würde nachdem der Thread terminiert hat, und man so effektiv WaitForSingleObject das Handle "unterm Hintern wegziehen" würde.
Du kannst entweder _beginthreadex verwenden, oder, wenn du einen MFC Thread brauchst, CREATE_SUSPENDED mitgeben, das m_bAutoDelete Member auf "false" setzen, und den Thread dann erst mit ResumeThread starten.
Beide Varianten funktionieren zuverlässig und sind gleichermassen "OK", die _beginthreadex finde ich persönlich allerdings wesentlich weniger umständlich
-
Und ich würde anstelle des int Killflags auch eine CEvent verwenden, um den Thread über wichtige Ereignisse zu informieren:
UINT BenchMarkThread(LPVOID param) { // threadcontrol.benchmarkstatus=true; // erübrigt sich, das Hauptprogramm kann auch auf das Thread-Ende mit Wait... warten while(WaitForSingleObject(threadcontrol.shutdown,0)==WAIT_TIMEOUT) { QueryPerformanceCounter(&benchmark.SP_PC_Start); Sleep(2000); double PSec_PC=double(benchmark.SP_PC_Stopp.QuadPart-benchmark.SP_PC_Start.QuadPart)/double(benchmark.PC_Freq.QuadPart); } // threadcontrol.benchmarkstatus=false; return 1; } void foo() { threadcontrol.globalshutdown.SetEvent(); WaitForSingleObject(threadcontrol.benchmarkThread->m_hThread,INFINITE); delete threadcontrol.benchmarkThread; }(damit letzteres zuverlässig funktioniert, mußt du (wie hustbaer schon angedeutet hat) das m_bAutoDelete Flag beim Thread-Start auf false setzen)
-
hustbaer schrieb:
p.S.: wenn du AfxBeginThread verwendest bekommst du ein CWinThread Objekt zurück wo das m_bAutoDelete Member auf "true" gesetzt ist. Dadurch zerstört der Thread bevor er sich beendet das Objekt automatisch, und das Thread-Handle wird freigegeben. In dem Fall darf man WaitForSingleObject nicht verwenden, da das Handle ja freigegeben wird bevor der Thread terminiert, WaitForSingleObject allerdings erst zurückkehren würde nachdem der Thread terminiert hat, und man so effektiv WaitForSingleObject das Handle "unterm Hintern wegziehen" würde.
Du kannst entweder _beginthreadex verwenden, oder, wenn du einen MFC Thread brauchst, CREATE_SUSPENDED mitgeben, das m_bAutoDelete Member auf "false" setzen, und den Thread dann erst mit ResumeThread starten.
Beide Varianten funktionieren zuverlässig und sind gleichermassen "OK", die _beginthreadex finde ich persönlich allerdings wesentlich weniger umständlichMal eine Frage dazu? Warum startest du den Thread erst mit CREATE_SUSPENDED, setzt dann m_bAutoDelete auf false und lässt ihn dann weiterlaufen? Ich hab das bisher immer so gemacht:
CWindThread* m_pThread; m_pThread=AfxBeginThread(ThreadFunc,this,THREAD_PRIORITY_NORMAL); m_pThread->m_bAutoDelete=false;Könnte das Probleme geben? Bisher hat es ja immer funktioniert...
@CStoll: was machst du denn mit dem Event im Thread? oder hast du da zu viel auskommentiert?
-
Die Methode mit CREATE_SUSPENDED geht in Ordnung und funktioniert!
-
AndyDD schrieb:
@CStoll: was machst du denn mit dem Event im Thread? oder hast du da zu viel auskommentiert?
Drauf "warten" - sorry, hatte den Kopf der while-Schleife versehentlich auskommentiert. (WaitForSingleObject(...,0) kehrt sofort zurück - entweder mit WAIT_OBJECT0 (das Ziel ist signalisiert) oder mit WAIT_TIMEOUT)
-
AndyDD schrieb:
Mal eine Frage dazu? Warum startest du den Thread erst mit CREATE_SUSPENDED, setzt dann m_bAutoDelete auf false und lässt ihn dann weiterlaufen? Ich hab das bisher immer so gemacht:
CWindThread* m_pThread; m_pThread=AfxBeginThread(ThreadFunc,this,THREAD_PRIORITY_NORMAL); m_pThread->m_bAutoDelete=false;Könnte das Probleme geben?
UINT ThreadFunc(void*) { printf("sepp\n"); } void foo() { CWindThread* m_pThread; m_pThread=AfxBeginThread(ThreadFunc,this,THREAD_PRIORITY_NORMAL); // context switch, thread läuft, thread beendet sich m_pThread->m_bAutoDelete=false; // <- BOOM! }
-
Martin Richter schrieb:
Die Methode mit CREATE_SUSPENDED geht in Ordnung und funktioniert!
Das ist mir schon klar. Ich fragte nach dem Warum zu dieser Vorgehensweise.
hustbaer schrieb:
UINT ThreadFunc(void*) { printf("sepp\n"); } void foo() { CWindThread* m_pThread; m_pThread=AfxBeginThread(ThreadFunc,this,THREAD_PRIORITY_NORMAL); // context switch, thread läuft, thread beendet sich m_pThread->m_bAutoDelete=false; // <- BOOM! }Ok, wenn die Threadfunktion returniert bevor man das AutoDelete auf false setzen konnte, dann seh ich das ein. Beide "Fäden" laufen ja quasi parallel. Aber wann tritt denn das auf? Lagert man nicht extra zeit- und prozessorlastraubende Dinge in einen solchen Workerthread aus? Dann dürfte der Thread ja immer länger leben als die Zugriffszeit aufs CWinThread-Objekt. Außerdem wird das von mir weiter oben gepostete Vorgehen auch in vielen Literaturstellen so angewendet. Klar, vertrauen kann man darauf nicht. Die von euch favorisierte Variante würde das abfangen.
-
Vielleicht hat der Thread ja eine andere Meinung davon, wie lange er benötigt als du - und nicht immer ist das so offensichtlich wie in diesem Beispiel
Und sobald es ans Mutlithreading geht, mußt du jederzeit damit rechnen, daß Thread A (das Hauptprogramm) schlafen gelegt und Thread B (der Worker) an den Prozessor gelassen wird.
-
AndyDD schrieb:
Ok, wenn die Threadfunktion returniert bevor man das AutoDelete auf false setzen konnte, dann seh ich das ein. Beide "Fäden" laufen ja quasi parallel. Aber wann tritt denn das auf? Lagert man nicht extra zeit- und prozessorlastraubende Dinge in einen solchen Workerthread aus? Dann dürfte der Thread ja immer länger leben als die Zugriffszeit aufs CWinThread-Objekt. Außerdem wird das von mir weiter oben gepostete Vorgehen auch in vielen Literaturstellen so angewendet. Klar, vertrauen kann man darauf nicht. Die von euch favorisierte Variante würde das abfangen.
1. Es tritt sehr schnell auf, wenn der Workerthread mit höherer Prio läuft und sofort loslegt und relativ schnell fertig wird, evtl. unerwartet weil eine Ressource nicht zur Verfügung steht.
2. So etwas nicht zu berücksichtigen, bzw. auch nicht den Fall, dass AfxBeginThread NULL returniert sind nette Fehler, die einen beim Einsatz beim Kunden den letzten Nerv rauben können.
3. Ich würde gerne Wissen welche Literaturquellen Du hier meinst? Ich würde diese Quellen sofort entsorgen, denn der Autor hat von Multithreading dann wirklich keine Ahnung.
-
Aber wann tritt denn das auf?
Immer genau dann wenn es am meisten Schaden anrichten kann.
-
hustbaer schrieb:
Aber wann tritt denn das auf?
Immer genau dann wenn es am meisten Schaden anrichten kann.
Auch ein "Jünger des Murphy"! Findet man selten heute, die meisten Programmierer heute gehen den Weg der sinnlosen Hoffung: "Es wird schon immer alles gut gehen"
