Multithreading und GetMessage



  • Martin Richter schrieb:

    Ich gehe in vielen Dingen nicht mit Joseph konform. Wie auch hier...
    Joseph ist/war (ich weiß es nicht) sehr speziell. Ich glaube am liebsten würde er es lieben wenn alle noch Assembler programmieren... 😉

    The Best Synchronization Is No Synchronization: Alternatives to semaphores, mutexes, and CRITICAL_SECTIONs

    http://www.flounder.com/no_synchronization.htm

    It is easier to reason about synchronization when you use protocols like Positive Handoff and Central Controller than if you try to reason about mutexes and semaphores. Because there is no explicit synchronization, there is no possibility of deadlock.

    Da ist schon was dran. Mit einem "Central Controller" bin ich immer sehr gut gefahren.



  • Th69 schrieb:

    Wohl aus Avoid CMutex, CEvent, CSemaphore and CCrticalSection, also die MFC-Implementierungen!!!

    In dem Artikel geht es hauptsächlich darum dass der Autor nicht verstanden hat wie man CSingleLock verwendet, daraus dann falsche Schlüsse zieht und behauptet dass CMutex und CCriticalSection "broken" wären. Das einzige was broken ist ist allerdings sein verständnis von CSingleLock .

    (OK, das ist nicht das einzige worum es in dem Artikel geht. Ein paar der anderen Punkte sind valide, aber für die meisten Anwendungen nicht von besonders grosser Bedeutung.)



  • Hallo zusammen,

    vielen Dank für eure zahlreichen Beiträge. 👍

    Bei mir ist es momentan leider etwas stressig, sodass ich nicht vor nächstem Wochenende dazu kommen werde, etwas zu programmieren. Mit euren zahlreichen Links sollte ich es aber schaffen, ich werde sowohl thread als auch die mutexes aus der Win API nehmen. Das sollte am Sinnvollsten sein.

    Wenn ihr also nicht noch weiter über den Autor des einen Links diskutieren wollt, könnt ihr das Thema schließen, ich steige bei der Diskussion leider aus.

    Ganz lieben Dank noch einmal!

    AnfängerX



  • Ich muss leider sagen, dass ich Mr. Newcomers Urteil weit mehr vertraue als hustbaers.



  • Es ist vollkommen egal wem du mehr vertraust. Das ist keine wischi-waschi Einschätzung wo es Meinungen geben kann. Das ist einfach so.
    CSingleLock ist wie std::unique_lock dafür gedacht dass man es auf den Stack legt. Lokale Variable innerhalb einer Funktion. Bzw. ganz explizit nicht dafür gedacht concurrent verwendet zu werden.

    Wer sich darüber aufregt dass es nicht thread-safe ist und/oder dass man damit nicht rekursiv locken kann, der hat das schlicht und ergreifend nicht verstanden.


  • Mod

    hustbaer schrieb:

    Th69 schrieb:

    Wohl aus Avoid CMutex, CEvent, CSemaphore and CCrticalSection, also die MFC-Implementierungen!!!

    In dem Artikel geht es hauptsächlich darum dass der Autor nicht verstanden hat wie man CSingleLock verwendet, daraus dann falsche Schlüsse zieht und behauptet dass CMutex und CCriticalSection "broken" wären. Das einzige was broken ist ist allerdings sein verständnis von CSingleLock .

    (OK, das ist nicht das einzige worum es in dem Artikel geht. Ein paar der anderen Punkte sind valide, aber für die meisten Anwendungen nicht von besonders grosser Bedeutung.)

    👍

    Genau... 🙂



  • EOP schrieb:

    Mit einem "Central Controller" bin ich immer sehr gut gefahren.

    ich habe mal etwas programmiert, bei dem sich die einzelnen threads bei einem "steuer-thread" in eine warteschleife eingetragen und dann aktiv mit sleep gewartet haben, bis sie an der reihe waren.

    ist das sowas? bing spuckt da irgendwie nur müll aus.



  • Bei mir ging es eigentlich nur um HTTP-threads.
    Die threads senden ihre Aufgabenanforderungen und Datenergebnisse per PostMessage an einen "Central Controller", der das verwaltet, Aufgaben zuteilt und neue Aufgabe versendet.
    Funktioniert auch mit 256 threads hervorragend und echt flott.



  • Hallo zusammen,

    lange ist es her, aber nachdem der Thread noch offen ist und ich am Verzweifeln, würde ich mich erneut sehr über eure Hilfe freuen.

    Grundsätzlich laufen die beiden Threads parallel ab und hypothetisch funktioniert es. In der Praxis jedoch sperrt Thread A, der die Auf- und Abbewegung des Balls steuert, ununterbrochene die Ressource bzw. das Fenster, sodass händische Eingaben nicht möglich sind, So ist es mir nicht möglich, das Programm zu schließen und ich sehe ununterbrochen das Windows-Ladesymbol.
    Implementiere ich in Thread A jedoch vor der Ressourcensperrung eine aufwändige Rechenaufgabe, kann ich das Programm wunderbar schließen oder andere händische Eingaben machen, nur hängt dann auch das Bild. 😞 😞

    #include <Windows.h>
    #include <tchar.h>
    #include <ctime>
    #include "Graphics.h"
    
    Graphics *bitte = NULL;
    CRITICAL_SECTION window;
    bool TAbbruch = false;
    
    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMSG, WPARAM wParam, LPARAM lParam) {
    
    	switch (uMSG)
    	{
    	case WM_CLOSE:
    		if (MessageBox(hwnd, _T("Are you sure?"), _T("noPlan"), MB_OKCANCEL) == IDOK) {
    			DestroyWindow(hwnd);
    			PostQuitMessage(0);
    		}
    		return 0;
    	}
    
    	return DefWindowProc(hwnd, uMSG, wParam, lParam);
    }
    
    DWORD WINAPI ThreadProc() {
    
    	float y = 150;
    	float ySpeed = 0.0;
    
    	time_t previous = time(0);
    	time_t lag = 0.0;
    
    	while (!TAbbruch)
    	{
    		/*time_t current = time(0);
    		time_t elapsed = current - previous;
    		previous = current;
    		lag += elapsed;
    
    			while (lag >= 0.0000000000001) {
    			*/
    
    				ySpeed += 3.0;
    				y += ySpeed;
    
    				if (y > 600) {
    					y = 600;
    					ySpeed = -50;
    				}
    
    				EnterCriticalSection(&window);
    				bitte->cls(1.0, 1.0, 1.0, 1.0);
    				bitte->Ball(375.0f, y, 90);
    				LeaveCriticalSection(&window);
    
    				//lag -= 0.0000000000001;
    			//}
    
    	}
    
    	return 0;
    }
    
    int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pcmdline, int nCmdShow)
    {
    	WNDCLASSEX wndc;
    	ZeroMemory(&wndc, sizeof(WNDCLASSEX));
    	wndc.cbSize = sizeof(WNDCLASSEX);
    	wndc.style = CS_HREDRAW | CS_VREDRAW;
    	wndc.lpfnWndProc = WindowProc;
    	wndc.hInstance = hInstance;
    	wndc.hbrBackground = (HBRUSH) COLOR_WINDOW;
    	wndc.lpszClassName = _T("MainWindow");
    
    	RegisterClassEx(&wndc);
    
    	RECT rect = { 0, 0, 800, 600 };
    	AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, false, WS_EX_OVERLAPPEDWINDOW);
    
    	HWND hwnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, wndc.lpszClassName, _T("DirectX"), WS_OVERLAPPEDWINDOW, 100, 100, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, NULL);
    
    	if (!hwnd) {
    
    		DWORD err = GetLastError();
    		TCHAR output[100];
    		_stprintf_s(output, _T("%s") , err);
    
    		MessageBox(NULL, (LPCWSTR) output, NULL, MB_OK);
    
    		return -1;
    	}
    
    	bitte = new Graphics();
    	if (!bitte->Init(hwnd)) {
    		delete bitte;
    		return -1;
    	}
    
    	ShowWindow(hwnd, nCmdShow);
    
    	InitializeCriticalSection(&window);
    
    	HANDLE thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ThreadProc, NULL, 0, NULL);
    
    	MSG msg = {};
    
    	while (GetMessage(&msg, NULL, 0, 0))
    	{
    
    		TranslateMessage(&msg);
    
    		EnterCriticalSection(&window);
    		DispatchMessage(&msg);
    		LeaveCriticalSection(&window);
    	}
    	TAbbruch = true;
    	Sleep(5000);
    
    	CloseHandle(thread);
    	DeleteCriticalSection(&window);
    	delete bitte;
    
    	return 0;
    }
    

    Ich bin um jeden Rat dankbar!



  • Ist doch klar: dein Thread ballert so schnell wie möglich durch die Schleife und hält dabei fast ununterbrochen die CriticalSection. Folglich bleibt fast keine Zeit mehr für die Nachrichtenschleife des Mainthreads; diese wird fast nur am Warten sein.

    PS: Du hast mindestens ein Data Race zwischen deinem Thread und dem Mainthread. Der Thread läuft auch noch, wenn das Fenster schon geschlossen wurde, folglich ist das HWND im Thread ungültig. TAbbruch muss außerdem atomic sein.


  • Mod

    Es gibt hier nur offene Threads. Also kannst Du auch einen neuen Anfangen!

    Also der Thread sollte nach Ausführung seines Zyklus seine time slice abgeben.

    Hier wäre Sleep(1) ein Kandidat. Gegen Sleep(0) spricht http://blogs.msdn.com/oldnewthing/archive/2005/10/04/476847.aspx aber würde auch gehen.

    @Biolunar: Eine Race Condition sehe ich nicht. Wo ist die? Beide wollen das gleiche. Und? Dafür gibt es die Critical Section!
    Oder verstehst Du was anderes under Race-Condition?
    https://de.wikipedia.org/wiki/Race_Condition

    Das Problem ist vermutlich einfach in der Priorisierung. Du möchtest das Inputs sofort behandelt werden. Also könntest Du die Thread Prio deines Input Threads erhöhen, und damit käme er öfters dran WENN eine Windows Nachricht ankommt.



  • Martin Richter schrieb:

    @Biolunar: Eine Race Condition sehe ich nicht. Wo ist die?

    if (!bitte->Init(hwnd)) { // Hier wird das HWND dem Graphics Objekt übergeben.
    
    // Im Thread:
    
    // Ich vermute mal, dass hier das HWND verwendet wird um das Fenster zu bemalen.
    EnterCriticalSection(&window);
    bitte->cls(1.0, 1.0, 1.0, 1.0);
    bitte->Ball(375.0f, y, 90);
    LeaveCriticalSection(&window);
    
    while (GetMessage(&msg, NULL, 0, 0)) 
    { 
        TranslateMessage(&msg); 
    
        EnterCriticalSection(&window); 
        DispatchMessage(&msg); 
        LeaveCriticalSection(&window); 
    } // Wenn das Programm aus diesem Block heraus geht, ist das HWND ungültig, weil das Fenster zerstört wurde.
    
    // Hier kann der Thread noch auf das ungültige HWND zugreifen und tut es vermutlich auch, da die
    // CriticalSection im Hauptthread verlassen wurde.
    


  • das mit dem ungültigen hwnd stimmt, dachte aber eigentlich die sleep-Funktion würde das lösen... atomic sagt mir nichts, ist das das: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476334(v=vs.85).aspx
    oder hättest du ein Beispiel?

    time slice sagt mir leider auch nichts und das mit der Priorität probiere ich gleich. und klar, ballert mein thread durch die Schleife in die CriticalSection, aber wenn er das nicht tut, hängt das Bild und das will ich ja auch nicht ... 😞

    ganz lieben Dank, freue mich über weitere Hilfe



  • LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMSG, WPARAM wParam, LPARAM lParam) {
    
    	switch (uMSG)
    	{
    	case WM_CLOSE:
    		if (MessageBox(hwnd, _T("Are you sure?"), _T("noPlan"), MB_OKCANCEL) == IDOK) {
    			TAbbruch = true;
    			Sleep(5000);
    			DestroyWindow(hwnd);
    			PostQuitMessage(0);
    		}
    		return 0;
    	}
    
    	return DefWindowProc(hwnd, uMSG, wParam, lParam);
    }
    [code="cli"]
    

    das ist vermutlich besser für den hwnd



  • Eine Einstellung der Prioritäten führt leider auch nicht zum Erfolg, ich sehe nur den Ladekringel und kann nichts drücken. Eine andere Idee?


  • Mod

    Kommt keine Nachricht an? Dann ist was anderes faul.
    Was war mit einem Sleep(1)?

    Bau mal einen Sleep(100) ein im Thread... dann muss ja was passieren.



  • Aber natürlich außerhalb der CriticalSection.

    Und für Grafikaktualisierung solltest du statt dem Thread besser einen Timer benutzen (der dann auch im UI-Thread läuft).



  • sleep 100, funktioniert, aber auch mit einem kleinen lag. Außerdem kriege ich jetzt wieder trotz meiner Modifikation einen hwnd-Fehler.

    Liegt mein Problem, dass entweder das Bild hängt oder keine Benutzereingaben verarbeitet werden können, an meiner Inkompetenz oder ist es mit multithreading tatsächlich nicht möglich? Kann ich die zu sperrende Ressource in CriticalSection irgendwie spezifizieren, sodass nicht das ganze Fenster gesperrt wird?

    @Th69, das Ziel dieses ganzen Vorhabens war eigentlich eine halbwegs vernünftige Übung zu multithreading zu haben...

    danke für eure unermüdliche Hilfe! 👍



  • Warum schreibst du das ganze Zeug nicht in die Callback-Funktion?
    Also CreateThread nach WM_CREATE und TAbbruch nach WM_DESTROY



  • @Wade1234: wahrscheinlich mache ich das falsch, aber bei mir führt das zu einem deadlock...

    und bei meiner anderen Frage, bitte keine Scheu...

    Danke für eure Hilfe!


Anmelden zum Antworten