Thread-Probleme im Release - Mode
-
Hallo liebes Forum,
wieder mal eine Frage:
Während meine Anwendung im debug-Mode tadellos funktioniert, habe ich im release-mode mit dem workerthread nur Probleme.
Das Problem tritt beim beenden des threads auf (es läuft weiter).
Die Anwendung läuft als "Zombie" selbst beim beenden des Programms weiter.Schalte ich in den Projekt-Einstellungen von NDEBUG auf _DEBUG um und manipuliere ein bischen an den Bibliothekseinstellungen funktioniert die
"Pseude - Release - Version".Ich versuche mal hier kurz zu erklären, was ich mache.
Hier ist eine FormView - Klassen - Deklaration, in dem der workerthread aktiv ist
... UINT wait4state(); static UINT thread_entry(LPVOID lpvoid) { return static_cast<CBpViewSoundPlay*>(lpvoid)->wait4state(); } void updateLstCtrl(int, int, const char*, const char*, const char*, const char*); void run() { TRACE0("RUN()\n"); m_hStopThread = CreateEvent(0, FALSE, FALSE, "StopThread"); m_hThreadStopped = CreateEvent(0, FALSE, FALSE, "ThreadStopped"); ::AfxBeginThread(thread_entry, this); TRACE0("END RUN()\n"); } void stop() { TRACE0("STOP()\n"); if ( m_hThreadStopped ) { ASSERT(SetEvent(m_hStopThread)); MSG msg; while ( true ) { DWORD dwResult = WaitForSingleObject( m_hThreadStopped, 0 ); if ( dwResult == WAIT_OBJECT_0 ) { // Thread hat sich beendet break; } if ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) { GetMessage( &msg, 0, 0, 0 ); TranslateMessage( &msg ); DispatchMessage( &msg ); } } } TRACE0("END STOP()\n"); } HANDLE m_hStopThread; HANDLE m_hThreadStopped;In der Implementation aktualisiere ich zyklisch in einem Thread eine ListView.
Beim Bedienen eine Dialogbar im MDI soll das Thread sich beenden und ein neues Thread sich starten.//----------------------------------------------------------------------------- LONG CBpViewSoundPlay::OnReInit(UINT, LONG) { char* pszIp = static_cast<CBpApp*>(AfxGetApp())->m_pszIp; if ( pszIp ) { stop(); m_lctlLoc.DeleteAllItems(); fillLbLoc(pszIp); m_lctlState.DeleteAllItems(); run(); } return 0L; } //-----------------------------------------------------------------------------Das funktioniert auch im Debug mode tadellos, nur nicht im Release - Modus.
Beim beenden arbeite ich mit Events und WaitForSingleObjectDie ist ein Teil der Implementation:
//----------------------------------------------------------------------------- UINT CBpViewSoundPlay::wait4state() { Database db; if ( db.connect(m_csConnectStr) ) { while ( true ) { ... mache ganz schön viel // Lese aus der Datenbank und aktualisiere eine ListView ... if ( WaitForSingleObject(m_hStopThread, 0) == WAIT_OBJECT_0 ) { SetEvent(m_hThreadStopped); break; } } db.disconnect(); } else m_hThreadStopped = 0; return 0; } //-----------------------------------------------------------------------------Es fällt mir etwas schwer die Logik verständlich zu erklären.
Es funktioniert etwa so (es ist die bewährte Thread - Logik mit Events):Das Thread wird gestartet:
...
Im thread wird das Event m_hStopThread mit WaitForSingleObject abgefragt.
Soll das Thread beendet werden, so wird dieses Event vom Hauptprogramm gesetzt mit SetEvent. Das Hauptprogramm wartet wiederum auf die Bestätigung, ob das Thread beendet wird/wurde mit WaitForSingleObject. Hier wird auf das Event
m_hThreadStopped gewartet. Dies wird in der Thread - Funktion mit SetEvent
gesetzt. Erst dann gibt das Hauptprogramm wenn gewünscht die Resourcen frei bzw. initialisiert ein anderes Thread.So die Theorie, die leider nur im debug - Mode (bei mir läuft).
Über jeden Tipp/Hilfe wäre ich dankbar.
Viele Grüße
Uli2
-
Du kannst nicht nur auf Events warten, sondern z.B. auch auf Thread-Handles (und ein Thread-Handle wird genau dann aktiv, wenn der Thread (wirklich endgültig) beendet ist). Das erspart dir einen Event zur Rückmeldung des Thread-Endes und außerdem verhindert es, daß du zwischen dem SetEvent() und dem db.disconnect() noch unterbrochen wirst.
-
Hallo,
vielen Dank für die prompte Antwort.
Ich habe dies jetzt mal experimentell eingebaut. Leider funktioniert es immer noch nicht.Hier ist mein Code:
... UINT wait4state(); static UINT thread_entry(LPVOID lpvoid) { return static_cast<CBpViewSoundPlay*>(lpvoid)->wait4state(); } void updateLstCtrl(int, int, const char*, const char*, const char*, const char*); void run() { m_hStopThread = CreateEvent(0, FALSE, FALSE, "StopThread"); CWinThread* pThread = ::AfxBeginThread(thread_entry, this); m_hThread = pThread->m_hThread; } void stop() { ASSERT(SetEvent(m_hStopThread)); WaitForSingleObject( m_hThread, INFINITE ); } HANDLE m_hThread; HANDLE m_hStopThread; ...und in der Implementation:
//----------------------------------------------------------------------------- UINT CBpViewSoundPlay::wait4state() { Database db; if ( db.connect(m_csConnectStr) ) { while ( true ) { ... // mache ganz schön viel ... if ( WaitForSingleObject(m_hStopThread, 0) == WAIT_OBJECT_0 ) break; } db.disconnect(); } return 0; } //-----------------------------------------------------------------------------Die Threadfunktion kehrt nicht zurück, also beendet sich nicht.
Viele Grüße
uli2
-
Ich empfehle mal den Debugger - wo läuft die Thread-Funktion lang, nachdem du den Stop-Event ausgelöst hast? (vielleicht dauert das "mache ganz schön viel" einfach so lange, daß er nichts von der Unterbrechung mitbekommen hat)
-
Hallo CStoll,
Das Hauptprogramm bleibt stehen, und zwar bei dem
WaitForSingleObject(m_hThread, INFINITE)Setze ich den 2. Parameter auf 1s, also 1000, so funktioniert es meistens.
Ich habe jetzt erstmal meine Variante mit den Events reaktiviert.
In dieser Variante funktioniert wenigstens die debug - Version.
Hier hängt alles an den defines _DEBUG bzw. NDEBUG.
Also eine release - Variante mit _DEBUG funktioniert auch.
Vielleicht liegt es an den speziellen "new" - Operator im debug-Modus.Ich habe momentan kein Plan, was ich noch machen kann.
Es gibt wohl auch noch Mutexe und CriticalSections, die man verwenden kann.Vielleicht sollte ich auch das Stück Code mit PeekMessage/GetMessage drin lassen.
Trotzdem erstmal danke.
Grüße
uli2