Thread mit MessageLoop

Thomasio

Erfahrenes Mitglied
Das ist völliges Neuland für mich und nachdem ich mich bei MSDN und via Google auch anderweitig etwas eingelesen habe, stehe ich völlig im Wald.
Von Messages die gar nicht ankommen, über DispatchMessage() nicht nutzbar, bis zu wie beendet man den Thread, ich blicke absolut nicht durch.

Für meine ersten Gehversuche will ich natürlich nichts Riesiges aufbauen, einfach nur die Grundstruktur.

Ich habe ein simples Win32 Programm, dessen Hauptfenster im Prinzip auch leer sein könnte, aber es hat natürlich eine WndProc und einen MessageLoop, ausserdem startet es meinen Thread, der hat kein eigenes Fenster und soll nur dann was tun, wenn er eine "MeineMessage" erhält.

Code:
const UINT MeineMessage = RegisterWindowMessage("SomeUniqueText");
DWORD MeineThreadID;

DWORD WINAPI MeinThread(LPVOID data)
{

MSG msg;

while(GetMessage(&msg,0,0,0) > 0)
     {
         // hier weiss ich nicht weiter
     }

return 0;

}

LRESULT CALLBACK MainWindow(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{

switch(msg)
{

...

case WM_IRGENDWAS:
{

PostThreadMessage(MeineThreadID,MeineMessage,wParam,lParam);

return 0;

}
break;

case WM_CLOSE:
{

DestroyWindow(hWnd);

return 0;

}
break;

case WM_DESTROY:
{

// hier muss auch der Thread beendet werden

PostQuitMessage(0);

return 0;

}
break;

}

return DefWindowProc(hWnd,msg,wParam,lParam);

}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR szCmdLine,int nCmdShow)
{

...

hwnd = CreateWindow(
                     "MeinFenster",
                     "MeinFensterName",
                     WS_OVERLAPPEDWINDOW,
                     0,0,400,300,
                     HWND_DESKTOP,0,hinstance,0
                   );

ShowWindow (hwnd, nCmdShow);
UpdateWindow(hwnd);

CreateThread(0,0,MeinThread,0,0,&MeineThreadID);

MSG msg;

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

return msg.wParam;

}

Ist jemand so nett und füllt mir den Code auf?
 
Hallo,

irgendwie versuchst du das Ganze gerade etwas kompliziert aufzuziehen. Der Thread soll eine langlaufende Aufgabe ausführen, ohne das dabei die GUI (also die WindowProc) blockiert wird. Da könnte man 2 Varianten realisieren:

1. Szenario:
Wenn dein Fenster eine Nachricht erhält, die eine solche Aufgabe erfordert, dann wird der Thread gestartet, arbeitet seine Aufgabe ab und beendet sich wieder. Benötigte Daten werden entweder als Parameter an den Thread übergeben oder vorher in einer globalen Datenstruktur hinterlegt.
C++:
LRESULT CALLBACK MainWindow(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
    switch(msg)
    {
        case WM_IRGENDWAS:
            CreateThread(0,0,MeinThread,&data,0,&MeineThreadID); // data ist eine beliebige Datenstruktur
            return 0;
    
        ...
    }
    
    return DefWindowProc(hWnd,msg,wParam,lParam);
 }

2. Szenario:
Der Thread muss permanent laufen, weil er z.B. irgendeine Kommunikationsschnittstelle bedienen muss. In dem Falle kann man den Thread über eine Statusvariable steuern. Das Verheiraten mit dem Windows-Nachrichtensystem funktioniert nicht, dazu braucht man ein "richtiges" Fenster. Umgekehrt funktioniert's aber: Der Thread kann Nachrichten an ein Fenster schicken.
C++:
#define TUE_NIX         0
#define TUE_DIES        1
#define TUE_DAS         2
#define SCHLEICH_DICH   3
 
int nStatus;

DWORD WINAPI MeinThread(LPVOID data)
{
    while( true )
    {
        switch( nStatus )
        {
            case TUE_DIES:
                // ...
                break;
                
            case TUE_DAS:
                // ...
                break;
                
            case SCHLEICH_DICH:
                return 0;
        }
        
        Sleep(1); // Verhindert Hohe CPU-Auslastung
    }
    
    return 0;
}
 
LRESULT CALLBACK MainWindow(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
    switch(msg)
    {
        case WM_IRGENDWAS:
            nStatus =  TUE_DIES;
            return 0;
   
        case WM_DESTROY:
            nStatus = SCHLEICH_DICH;
            PostQuitMessage(0);
            return 0;
   
        ...
    }
    
    return DefWindowProc(hWnd,msg,wParam,lParam);
 }
 
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR szCmdLine,int nCmdShow)
{
    ...

    int nStatus = TUE_NIX; 
    CreateThread(0,0,MeinThread,0,0,&MeineThreadID);
}

Was ich in den Codebeispielen nicht berücksichtigt habe, ist die Absicherung globaler Datenstrukturen bei sogenannten kokurrierenden Zugriffen. Dafür könnte man z.B. einen Mutex nehmen.

Gruß
MCoder
 
Hi MCoder,

vielen Dank für deine Vorschläge, das macht Sinn.

Nur eine blöde Zusatzfrage:
Ich stelle mir einfach vor, wenn der Thread in Szenario 2 bis zu 1000 Mal pro Sekunde durch den Loop muss, dann verursacht das doch auch eine Menge CPU Auslastung, oder nicht?
Aber vermutlich hat ein GetMessage() auch irgendwo intern eine entsprechende Warteschleife, so dass es kaum einen Unterschied macht?
Warum verhindert ein Sleep(1) überhaupt eine hohe CPU Auslastung?
Klar, bei 3 Ghz gibt das im Verhältnis zum CPU Takt eine deutliche Pause, aber es erscheint mir doch arg kurz.
Allerdings bin ich sehr dafür, wenn die Pause so kurz wie möglich ist, einfach weil das Programm dann schneller läuft.
 
Hallo Thomasio,

das Sleep() macht nicht einfach nur eine Pause, sondern es gibt auch Rechenzeit ab, so dass dem systemweiten Thread-Scheduler die Arbeit erleichtert wird. Die Länge der Pause ist dabei unwesentlich, es geht nur darum, dass überhaupt eine gemacht wird.

Gruß
MCoder
 
Zurück