WinAPI C/C++ - Warten das Thread beendet ist

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
gombolo
Establishment
Beiträge: 161
Registriert: 26.01.2011, 20:33

WinAPI C/C++ - Warten das Thread beendet ist

Beitrag von gombolo »

Hallo zusammen,

seit ein paar Tagen (mit Unterbrechung) bekomme ich ein mögliches Timing-Problem nicht hin. Ich erstelle ein Fenster und einen zweiten Thread in dem ich ein paar Operationen ausführe. Das Fenster macht nichts und soll auf den Thread warten.

Im Debug funktioniert es. Jetzt habe ich auf Release umgestellt und booommmm...das Fenster schließt sofort nach Programmstart.

Ich bin mir jetzt nicht sicher ob der Compiler im Release was optimiert oder im Release es einfach zu schnell ist und ich ein Timing-Problem habe. Vielleicht hat jemand einen Hinweis woran das liegen könnte. Danke!

Die Nachrichtenschleife sieht so aus:

Code: Alles auswählen

	while (true)
	{
		// Retrieve messages
		if (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
		{
			// If the message is WM_QUIT, exit the loop
			if (msg.message == WM_QUIT)
				break;

			// Translate and dispatch the message
			::TranslateMessage(&msg);
			::DispatchMessage(&msg);
		}
		else
		{
			// Check if the thread has terminated
			DWORD result = ::WaitForSingleObject(hThread, INFINITE);
			if (result == WAIT_OBJECT_0)
			{
				// Thread has terminated
				DWORD exitCode;
				if (::GetExitCodeThread(hThread, &exitCode) && exitCode == 0)
				{
					// Successfully terminated
					break;
				}
				else
				{
					// Error while terminating the thread
				}
			}
		}
	}
NytroX
Establishment
Beiträge: 387
Registriert: 03.10.2003, 12:47

Re: WinAPI C/C++ - Warten das Thread beendet ist

Beitrag von NytroX »

Ich weiß zwar nicht, ob es der Grund für das Beenden deines Programmes ist, aber dass das Fenster auf den Thread wartet ist meiner Meinung nach ein grundsätzliches Problem; ich glaub das geht so nicht.

Das Fenster bekommt immer alle möglichen Nachrichten in eine Queue, die es verarbeiten MUSS. Ist die Queue voll, stellt Windows fest, dass das Programm keine Nachrichten mehr verarbeitet und schießt es ab (man erinnert sich früher an die Meldung "Andwendung reagiert nicht mehr...").

Was du machst ist ja folgendes:

Code: Alles auswählen

while(true) {
 if (Nachricht da) {
   // verarbeiten
 } else {
   // warte unendlich -- uuhm, was ist mit Nachrichten, die während dem Warten reinkommen?
 }
}
Du blockierst mit der Warterei ja den kompletten MessageLoop, ich denke mal das ist auf jeden Fall ein Problem.

Versuch doch mal
::WaitForSingleObject(hThread, 0);
Also dass er garnicht wartet, und du prüfst einfach danach ob es fertig ist.

Sobald das Fenster existiert muss es auf jeden Fall durchgehend Nachrichten verarbeiten, sonst hängt die Anwendung aus Sicht von Windows.
Das Fenster IST quasi die Anwendung.
Die Frage, die du dir also stellen musst, ist, was genau du mit "Das Fenster wartet auf den anderen Thread" meinst.
Heißt das, du willst es nicht anzeigen/ausblenden ? Man soll es nicht schließen / nicht damit interagieren können ?
Warum wartest du nicht mit dem Öffnen des Fensters bis der andere Thread fertig ist?
Benutzeravatar
gombolo
Establishment
Beiträge: 161
Registriert: 26.01.2011, 20:33

Re: WinAPI C/C++ - Warten das Thread beendet ist

Beitrag von gombolo »

jetzt muss ich auf meine eigene Frage antworten. Im Release scheint der zweite Thread zu schnell durchzulaufen. Wenn ich die den Ablauf etwas verzögere funktioniert es wieder...finde ich trotzdem etwas seltsam... :)
Benutzeravatar
gombolo
Establishment
Beiträge: 161
Registriert: 26.01.2011, 20:33

Re: WinAPI C/C++ - Warten das Thread beendet ist

Beitrag von gombolo »

NytroX hat geschrieben: 24.02.2024, 12:01 ...
Versuch doch mal
::WaitForSingleObject(hThread, 0);
Also dass er garnicht wartet, und du prüfst einfach danach ob es fertig ist.

Sobald das Fenster existiert muss es auf jeden Fall durchgehend Nachrichten verarbeiten, sonst hängt die Anwendung aus Sicht von Windows.
Das Fenster IST quasi die Anwendung.
Die Frage, die du dir also stellen musst, ist, was genau du mit "Das Fenster wartet auf den anderen Thread" meinst.
Heißt das, du willst es nicht anzeigen/ausblenden ? Man soll es nicht schließen / nicht damit interagieren können ?
Warum wartest du nicht mit dem Öffnen des Fensters bis der andere Thread fertig ist?
ich habe gerade die Nachrichtenschleife überarbeitet und es genau so gemacht wie du es beschrieben hast. :)
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: WinAPI C/C++ - Warten das Thread beendet ist

Beitrag von Krishty »

Was NytroX schreibt. Dein zweiter Thread soll sich nicht einfach beenden, sondern zuvor als letzte Amtshandlung via PostMessage() dem Fenster mitteilen, dass er fertig ist. Als Nachrichten-ID nimmst du dabei den Bereich kurz hinter WM_APP; der ist für Anwendungs-interne Nachrichten reserviert. (Falls das Fenster speziell dafür gebaut wurde und das Warten Teil seiner Schnittstelle ist, geht auch WM_USER.)

Erst, wenn das Fenster diese Nachricht erhält, wartet es auf das Beenden des Threads und zieht seinen Rückgabewert ab. Damit verschwindet WaitForSingleObject() aus deiner Haupt-Nachrichtenschleife.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
gombolo
Establishment
Beiträge: 161
Registriert: 26.01.2011, 20:33

Re: WinAPI C/C++ - Warten das Thread beendet ist

Beitrag von gombolo »

EDIT: ok jetzt habe ich das über die Nachrichten auch eingebaut...

#define WM_THREADFINISHED (WM_APP + 1)

viel besser, dann kann ich auch den Rückgabewert des Threads verarbeiten...perfekt !!!

Meint ihr so:

Code: Alles auswählen

	
	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	WaitForSingleObject(hThread, INFINITE);
	
	// Close the thread handle
	if(hThread)
		::CloseHandle(hThread);
		
Funktioniert auch gut. Mein Code ist jetzt übersichtlicher und cleaner... :)

Die Version geht auch:

Code: Alles auswählen

	DWORD exitcode;
	while (true)
	{
		if (!::GetMessage(&msg, nullptr, 0, 0))
			break;
	
		DWORD result = WaitForSingleObject(hThread, 0);
		if (result == WAIT_OBJECT_0)
		{
			// The thread has been closed
			BOOL rc = GetExitCodeThread(hThread, &exitcode);
			if (!rc)
			{
				// Error handling
			}
			else
			{
				// It can be closed.
				::PostQuitMessage(0);
			}
		}
	
		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
	}
aber mir gefällt die kürzere Variante besser
NytroX
Establishment
Beiträge: 387
Registriert: 03.10.2003, 12:47

Re: WinAPI C/C++ - Warten das Thread beendet ist

Beitrag von NytroX »

Hmmja, in der WinAPI Doku steht explizit:
Because the return value can be nonzero, zero, or -1, avoid code like this:
while (GetMessage( lpMsg, hWnd, 0, 0)) ...
Ich sehe auch ein Problem mit der GetMessage Funktion - die returned ja nur, wenn das Fenster Nachrichten bekommt.
Was machst du, wenn das Fenster keine Nachrichten bekommt? Ist dann egal, ob der Hintergrund-Thread schon fertig ist?
Du wartest ja quasi bis jemand mit dem Fenster interagiert und prüfst erst dann den Hintergrund-Thread.

Irgendwie hatte ich deinen ersten Beitrag anders verstanden, oder mir ist dein Anwendungsfall noch nicht klar.
Vielleicht kannst du ja nochmal beschreiben was genau du am Ende erreichen willst.

Du hast also ein Fenster, dass die ganze Zeit über nichts machen soll, und sich dann schließt wenn der andere Thread fertig ist?
D.h. du willst den 2. Thread nur haben, damit die GUI responsive bleibt?
Also man kann mit dem Fenster rumspielen (minimieren, aufs "X" drücken, usw.) um... was genau zu machen?

Du kannst übrigens anstatt des Windows-API Geraffels für den Hintergrund-Thread auch einfach C++ nehmen.
Hier mal ein Beispiel (ungetestet, aber so ähnlich könnte es gehen):

Code: Alles auswählen

auto hintergrundThread = std::async(std::launch::async, hintergrundThreadFunktion);
MSG msg = { };
while(hintergrundThread.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
{
    // MessageQueue leeren und Messages verarbeiten
    while (PeekMessage(&msg, NULL,  0, 0, PM_REMOVE)) {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
    // dem Windows Scheduler mitteilen, dass jetzt andere Threads laufen können - vermeidet "busy wait"
    Sleep(0);
}
auto hintergrundThreadReturnValue = hintergrundThread.get();

//...

int hintergrundThreadFunktion() {
    return 0;
}
Vollständiger Code auf Godbolt: https://godbolt.org/z/Gc8aP6j4s

Ganz so gut ist aber das "Sleep(0)" noch nicht. Vielleicht also doch mit GetMessage und dafür sorgen, dass dort eine Message regelmäßig reinkommt.
Also WM_TIMER oder sowas. Aber für so eine einfache Sache wird die Lösung langsam echt komplex :-P

Quellen:
https://learn.microsoft.com/en-us/windo ... getmessage
https://learn.microsoft.com/en-us/windo ... sage-queue
https://learn.microsoft.com/en-us/windo ... hapi-sleep
Benutzeravatar
gombolo
Establishment
Beiträge: 161
Registriert: 26.01.2011, 20:33

Re: WinAPI C/C++ - Warten das Thread beendet ist

Beitrag von gombolo »

bitte nicht lachen, aber ich programmiere eine 3D Engine....ja wirklich. Ich mache das nicht zum ersten mal. Ich habe das schon mal vor paar Jahren gemacht.

meine 3D Engine leider ohne Screenshots...

Ich brauche das am Abend. Ich habe einen "langweiligen" Job und Abends setze ich mich hin und entspanne mich so. Mal programmiere ich eine 3D Engine und mal ein Spiel und ein anderes mal mache ich was mit Blender... :)

Dabei erheben ich keinen Anspruch darauf das ich die Programmierung sehr gut beherrsche.

Nun zu meinem Vorhaben:

Die Idee ist es ein Fenster zu erstellen, dann einen Thread in dem die Engine läuft. Es ist im wahrsten Sinne des Wortes ein selbstlaufendes Programm. Also keine Ansammlung von Funktionen. Dabei ist die Verarbeitung der Eingaben über das Fenster irrelevant. Es existiert nur um die Bilder der 3D Engine darzustellen.

und ja du hast Recht, aber ich würde den Thread in C++ so starten:

Code: Alles auswählen

std::thread mainThread(main, reinterpret_cast<LPVOID>(hwnd)); 
Zuletzt geändert von gombolo am 29.02.2024, 09:52, insgesamt 1-mal geändert.
NytroX
Establishment
Beiträge: 387
Registriert: 03.10.2003, 12:47

Re: WinAPI C/C++ - Warten das Thread beendet ist

Beitrag von NytroX »

bitte nicht lachen, aber ich programmiere eine 3D Engine
Warum soll ich lachen, macht doch quasi jeder hier :-) ist halt einfach Hobby...

Achso, d.h. du willst quasi nur das "present" im Fenster-Thread machen und alles andere getrennt davon, jetzt hab ichs verstanden :-)
Da eignen sich natürlich die neuen APIs (DX12/Vulkan/Metal) gut für, weil man da ja die CommandBuffers in anderen Threads befüllen kann, dann hast du im Window-Thread nur das Rendern/Submit und Presenten.
Macht auf jeden Fall Sinn, würde ich sagen.
Mit Steuerung musst du mal ausprobieren, die ist glaube ich auch relativ abhängig vom Fenster - aber du kannst die Events ja an die Engine weiterleiten.

Dann kannst du je nach Anwendungsfall tatsächlich auch einfach GetMessage nehmen, weil du ja vermutlich per fertigem Frame sowieso ein WM_PAINT bekommst.

Wünsche dir auf jeden Fall viel Glück bei deiner Engine - und vor allem viel Spaß und Motivation :-)
Benutzeravatar
gombolo
Establishment
Beiträge: 161
Registriert: 26.01.2011, 20:33

Re: WinAPI C/C++ - Warten das Thread beendet ist

Beitrag von gombolo »

NytroX hat geschrieben: 28.02.2024, 21:49 Mit Steuerung musst du mal ausprobieren, die ist glaube ich auch relativ abhängig vom Fenster - aber du kannst die Events ja an die Engine weiterleiten.
das mache ich über DirectX Input...ich brauche praktisch keine Infos vom Fenster. Bis auf die Abfrage ob es noch existiert. Wenn das Fenster geschlossen wird, dann erhält die Engine diese Info und kann runterfahren und den Thread beenden.
NytroX hat geschrieben: 28.02.2024, 21:49 Wünsche dir auf jeden Fall viel Glück bei deiner Engine - und vor allem viel Spaß und Motivation :-)
Danke :D
Antworten