Thread der endet aber nicht beendet ?
-
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"
