Warten auf Thread ..



  • hi,

    da durch meine while-Schliefe die Message-Loop meines Programmes nicht mehr erreicht wird, hab ich mal die Abfrage der Warteschlange in meine while-Schleife verlagert:

    while ( WaitForSingleObject( ht, 10 ) == WAIT_TIMEOUT ) {
       SendMessage ( hwnd_progressbar, PBM_STEPIT, (WPARAM) 0, (LPARAM) 0);
       if (PeekMessage ( &dummy_msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE )) {
          DispatchMessage ( &dummy_msg );
       }
    }
    

    Ich hab jetzt zumindest den Erfolg, dass das Hauptfenster mit seinen Steuerelementen immer neu gezeichnet wird. Leider kann ich das Fenster aber nicht verschieben, da nur WM_PAINT verarbeitet wird.

    Nachdem ich noch einen weiteren Nachrichtenbereich verarbeite, kann ich dan auch das Fenster verschieben:

    while ( WaitForSingleObject( ht, 10 ) == WAIT_TIMEOUT ) {
       SendMessage ( hwnd_progressbar, PBM_STEPIT, (WPARAM) 0, (LPARAM) 0);
       if (PeekMessage ( &dummy_msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE )) {
          DispatchMessage ( &dummy_msg );
       }
       if (PeekMessage ( &dummy_msg, NULL, WM_NCCREATE, WM_NCMBUTTONDBLCLK,  PM_REMOVE )) {
          DispatchMessage ( &dummy_msg );
       }
    }
    

    Eigentlich funktioniert das jetzt so wie ich es möchte. Frag sich nur ob sich bei einigen bei dieser Methode die Magenwände zusammenziehen....

    Gruss d_A

    Hier mal den Quellcode mit dem ich teste:

    #include <windows.h>
    #include <process.h>
    #include <commctrl.h>
    
    // PROTOTYPING
    LRESULT CALLBACK Main_WndProc  ( HWND, UINT, WPARAM, LPARAM );
    static void Register_All       ( HINSTANCE );
    
    DWORD WINAPI     start_thread  ( PVOID );
    
    // HAUPTFUNKTION
    int WINAPI WinMain ( HINSTANCE hInst, HINSTANCE hPrevInstance, PSTR lpszCmdLine, int nCmdShow )
    {
    
       MSG msg;
       HWND hwnd_Main;
    
       if ( !hPrevInstance ) {
          Register_All ( hInst );
       }
       InitCommonControls ();
    
       // HAUPTFENSTER CREIEREN
       hwnd_Main = CreateWindowEx ( 0L, "MAIN_WINDOW", "Version -1 ;-)", 
                                    WS_OVERLAPPEDWINDOW 
                                    | WS_VISIBLE
                                    | WS_CLIPCHILDREN,
                                    CW_USEDEFAULT, CW_USEDEFAULT,
                                    300, 200,
                                    NULL, NULL, hInst, NULL );
    
       ShowWindow ( hwnd_Main, nCmdShow );
       UpdateWindow ( hwnd_Main );
    
       while (GetMessage ( &msg, NULL, 0, 0 )) {
          TranslateMessage ( &msg );
          DispatchMessage ( &msg );
       }
    
       return ( msg.wParam );
    
    }
    
    //
    // WINDOW-PROZEDUR FUER HAUPTFENSTER
    //
    LRESULT CALLBACK Main_WndProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
    {
    
       HDC hdc;
       PAINTSTRUCT ps;
       static HWND hwnd_progressbar, hwnd_button;
       static DWORD  t_id;
       static HANDLE ht;
    
       switch ( message ) {
          case WM_CREATE:
             t_id = (DWORD) 0;
             ht   = (HANDLE) 0;
    
             hwnd_progressbar =  CreateWindowEx ( 0L, 
                                                  "msctls_progress32", 
                                                  "",
                                                  WS_CHILD | WS_VISIBLE,
                                                  0, 0,  100, 20,
                                                  hwnd, 
                                                  (HMENU) 1, 
                                                  (HINSTANCE) ((LPCREATESTRUCT) lParam)->hInstance,
                                                  NULL );
             hwnd_button =  CreateWindowEx ( 0L, 
                                             "BUTTON", 
                                             "Start",
                                             WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                             105, 0,  50, 20,
                                             hwnd, 
                                             (HMENU) 2, 
                                             (HINSTANCE) ((LPCREATESTRUCT) lParam)->hInstance,
                                             NULL );
    
             SendMessage ( hwnd_progressbar, PBM_SETRANGE, (WPARAM) 0, (LPARAM) MAKELPARAM (0, 100));
             SendMessage ( hwnd_progressbar, PBM_SETSTEP, (WPARAM) 1, (LPARAM) 0);
    
          break;
          case ( WM_PAINT ):
             hdc = BeginPaint ( hwnd, &ps );
             EndPaint ( hwnd, &ps );
             return ( 0 ) ;
          break;
          case ( WM_COMMAND ):
             switch ( LOWORD (wParam) ) {
                case ( 2 ):
                   switch ( HIWORD (wParam ) ) {
                      case ( BN_CLICKED ):
                         SendMessage ( hwnd_progressbar, PBM_SETPOS, (WPARAM) 0, (LPARAM) 0);
                         if( ( ht = (HANDLE) _beginthreadex ( NULL, 0, start_thread, (void *) 0, 0, &t_id )) != (HANDLE) 0 ) {
                            MSG dummy_msg;
                            while ( WaitForSingleObject( ht, 10 ) == WAIT_TIMEOUT ) {
                               SendMessage ( hwnd_progressbar, PBM_STEPIT, (WPARAM) 0, (LPARAM) 0);
    
                               if (PeekMessage ( &dummy_msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE )) {
                                  DispatchMessage ( &dummy_msg );
                               }
                               if (PeekMessage ( &dummy_msg, NULL, WM_NCCREATE, WM_NCMBUTTONDBLCLK, PM_REMOVE )) {
                                  DispatchMessage ( &dummy_msg );
                               }
                            }
                            CloseHandle ( ht );
                            t_id = (DWORD) 0;
                            ht   = (HANDLE) 0;
                         }
                         SendMessage ( hwnd_progressbar, PBM_SETPOS, (WPARAM) 0, (LPARAM) 0);
                         return ( 0 );
                      break;
                   }
                break;
    
             }
          break;
          case WM_DESTROY:
             PostQuitMessage ( 0 );
             return ( 0 );
          break;
    
       }
    
       return ( DefWindowProc ( hwnd, message, wParam, lParam ) );
    
    }
    
    // 
    // REGISTRIEREN
    // 
    static void Register_All ( HINSTANCE hInst )
    {
    
       WNDCLASSEX win;
    
       // HAUPTFENSTER
       ZeroMemory ( &win, sizeof ( win ));
    
       win.cbSize = sizeof ( win );
       win.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
       win.lpfnWndProc = Main_WndProc;
       win.cbClsExtra = 0;
       win.cbWndExtra = 0;
       win.hInstance = hInst;
       win.hCursor = LoadCursor ( NULL, IDC_ARROW );
       win.hbrBackground = (HBRUSH) (COLOR_WINDOW);
       win.lpszMenuName = NULL;
       win.lpszClassName = "MAIN_WINDOW";
       RegisterClassEx ( &win );
    
    }
    
    /********************************************************************/
    /*                                                                  */
    /********************************************************************/
    DWORD WINAPI start_thread ( PVOID pParam )
    {
       Sleep ( 20000 ); // habe viel zu tun :-)
    
       MessageBox ( NULL, "Tread wird jetzt beendet", "Hallo", MB_ICONINFORMATION );
    
       _endthreadex( 0 );
       return ( (DWORD) 0 );
    
    }
    


  • Etwas Delphi Code:

    repeat
        waitResult := MsgWaitForMultipleObjects(1, hThread, False, INFINITE, MsgMask);
        if waitResult = WAIT_OBJECT_0 + 1 then
          ProcessMessages(0);
      until waitResult = WAIT_OBJECT_0;
    
    procedure ProcessMessages(hWnd: DWORD);
    var
      Msg                    : TMsg;
    begin
      while PeekMessage(Msg, hWnd, 0, 0, PM_REMOVE) do
      begin
        TranslateMessage(Msg);
        DispatchMessage(Msg);
      end;
    end;
    

    So sollte es gehen.



  • Ich weiß jetzt nicht ganz genau, ob das nicht schon angesprochen wurde - aber ich mache das immer so, dass mein Thread selbst eine Funktion meines Dialoges aufruft welche dann den Fortschrittsbalken setzt...
    Eigentlich ruft mein Thread eine Funktion meiner Klasse auf. Mittels einem Klassenpointer und einem Klassenmethodenpointer..
    Dann hab ich Zugriff auf alles und muss nichts static machen was es nicht braucht.

    Wenn du dafür Code haben willst - sag einfach Bescheid.



  • der_anhalter schrieb:

    Eigentlich funktioniert das jetzt so wie ich es möchte. Frag sich nur ob sich bei einigen bei dieser Methode die Magenwände zusammenziehen....

    Zumindest bei mir ist dies der Fall. 🙂
    Wie du es momentan machst, wirkt wie ein einziger Hack. Die Nachrichten sollten nur an einer Stelle abgefertigt werden, eben in deinem Message Loop. Der Nachrichten Handler selbst ist dafür da, um die Nachrichten zu verarbeiten und nicht um sich ständig wiederholende Aktionen auszuführen. Du solltest die Abfrage des Threads auf jedenfall nach aussen legen. Oder du machst es wie hehejo sagt und lässt den Thread selbst einen "Update-Handler" ausführen. Ist letztendlich gehüpft wie gesprungen. Nur bitte ersetze

    WaitForSingleObject( ht, 10 ) == WAIT_TIMEOUT
    

    durch

    WaitForSingleObject(ht, 0) != WAIT_OBJECT_0
    

    Es gibt keinen Grund, den aktuellen Thread unnötig auszubremsen.



  • hi,

    ... mal die Frage anders formuliert:

    ich muss in meinem Programm eine Funktion aufrufen die unter Umständen mehr als 2 Minuten Laufzeit benötigt. Der Anwender sollte aber über einen Fortschrittsbalken informiert werden. Was kann man tun ?
    Ich habe selbst keinen Einfluss auf diese Funktion sondern kann sie nur starten und warten.

    Da ich nun die Funktion selbst nicht verändern kann, besteht also keine Möglichkeit in den Ablauf einzugreifen um dort eine Funktion einzuklinken, die dann einen Forschrittsbalken setzt.

    Aus diesem Grund habe ich die Funktion in einen Thread gepackt um dann mit

    while ( WaitForSingleObject( ht, 10 ) == WAIT_TIMEOUT ) { 
       SendMessage ( hwnd_progressbar, PBM_STEPIT, (WPARAM) 0, (LPARAM) 0); 
    }
    

    eine Möglichkeit zu haben um den Forschrittsbalken zu setzen. Dadurch bekommt aber mein Programm keine WM_PAINT-Messages mehr. Aus diesem Grund hab ich dann die Schleife erweitert.

    Jetzt sieht sie so aus:

    while ( WaitForSingleObject( ht, 10 ) == WAIT_TIMEOUT ) {
       SendMessage ( hwnd_progressbar, PBM_STEPIT, (WPARAM) 0, (LPARAM) 0);
    
       /* Klick auf den Rahme etc. erlauben  */
       if (PeekMessage ( &dummy_msg, NULL, WM_NCCREATE, WM_NCMBUTTONDBLCLK, PM_REMOVE )) {
          /* Programm schliessen vermeiden... */
          if ( dummy_msg.message == WM_NCLBUTTONDOWN ) {
             if ( dummy_msg.wParam != HTCLOSE ) {
                DispatchMessage ( &dummy_msg );
             }
          } else {
             DispatchMessage ( &dummy_msg );
          }
       }
       /* WM_PAINT verarbeiten              */
       if (PeekMessage ( &dummy_msg, NULL, 0, 0, PM_REMOVE )) {
          if ( dummy_msg.message == WM_PAINT ) {
             DispatchMessage ( &dummy_msg );
          }
       }
    }
    

    Wenn ich Einfluss auf die Funktion hätte, dann könnte ich es ja so machen wie hehejo geschrieben hat. Leider kann ich diese besagte Funktion nur aufrufen und dann warten bis sie fertig ist. Naja, und deshalb eben dieser Hack damit sich doch was bewegt...

    Gruss d_A



  • der_anhalter schrieb:

    ich muss in meinem Programm eine Funktion aufrufen die unter Umständen mehr als 2 Minuten Laufzeit benötigt. Der Anwender sollte aber über einen Fortschrittsbalken informiert werden. Was kann man tun ?
    Ich habe selbst keinen Einfluss auf diese Funktion sondern kann sie nur starten und warten.

    Im Grunde ist das ungünstig, weil du so nur anzeigen kannst, dass deine Funktion aktiv ist. Eine echte Fortschrittsanzeige hast du aber nicht. Wenn du keinen Einfluss auf die Funktion hast, dann mach es so, wie ich bereits mehrfach geschrieben habe. Dafür musst du deinen Message Loop mehr einem typischen Game Loop anpassen.

    for (;;)
    {
    	if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    		if (msg.message == WM_QUIT)
    			break;
    	}
    	do_something();
    }
    

    In do_something() machst du dann alles, was zyklischer Bearbeitung bedarf. Zur Not kannst du auch noch benötigte Parameter übergeben, wie zB das Handle des Hauptfensters.



  • hi,

    groovemaster schrieb:

    Im Grunde ist das ungünstig, weil du so nur anzeigen kannst, dass deine Funktion aktiv ist. Eine echte Fortschrittsanzeige hast du aber nicht.

    Ja, das ist richtig.
    Da ich das Ergebnis der Funktion brauche um in meinem Programm fortzufahren, muss ich warten, und will dem Anwender nur mitteilen dass sich noch was bewegt. Eine echte Fortschrittanzeige ist das nicht.

    Ich verstehe nur nicht was es bringen soll meine Message Loop zu verändern, denn wenn in "do_something" die Funktion aufgerufen wird die so lange braucht, dann steht wieder alles und wartet ....

    Gruss d_A



  • wenn du deine Loop nich anfassen willst, und auf die funktion eh keinen einfluss hasst ... liegt eigentlich nur eines nahe:

    du brauchst nen asynchronen aufruf damit deine loop ned blockst, du brauchst nen assynchronen Aufruf um deine function auszufuehren und trotzdem "irgendeinen" Status in der Zeit liefern zu koennen -> du brauchst 2 Thread's !

    du rufst deine Funktionalitaet von der GUI auf -> 1. Thread starten, rueckkehr !
    dein 1. Thread ruft nen weitern Thread auf, der nur die ominoese funktion startet.
    dein erster thread arbeitet in ner schleife, wo er die timeouts fuer die Statusanzeige verwendet und gleichzeitig den Status des 2. threads ueberwacht ...
    iss der 2. thread fertig, gibts noch schnell ne statusmeldung und dein 1.thread beendet sich auch ....

    Klingt komplizierter als es ist ....

    Ciao ...



  • RHBaum schrieb:

    du rufst deine Funktionalitaet von der GUI auf -> 1. Thread starten, rueckkehr !
    .

    ich möchte nicht komplett zurückkehren, da das Ergebnis der ominoesen Funktion gebraucht wird. Der Anwender darf in der Laufzeit der Funktion keine Buttons, Menues bedienen. Wenn ich zurückkehre könnte er das aber wieder tun.

    Es bleibt eigentlich nur ein Problem:
    die Anzeige des Hauptfensters wird nicht aktualisiert während ich auf das Ende der Funktion warte.

    Es ist doch fast das Gleiche wie die Funktion DialogBox (...). Wenn ich einen Dialog mit dieser Funktion auf den Schirm bringe, dann kann der Anwender nur diesen Dialog bedienen (Modales Dialogfeld). Das Hauptfenster wird aber weiterhin immer brav neu gezeichnet wenn ich mit einem anderen Fenster drüberfahre.
    Wieso eigentlich ?
    Die DialogBox-Funktion kehrt erst nach Beendigung des Dialoges wieder und die Main-Message-Loop wird erst dann wieder abgearbeitet.

    Habe gerade gelesen dass der Dialog Manager ne eigene Message Loop besitzt. Wäre ja mal interessant wie die aussieht...

    Gruss d_A



  • der_anhalter schrieb:

    Ich verstehe nur nicht was es bringen soll meine Message Loop zu verändern, denn wenn in "do_something" die Funktion aufgerufen wird die so lange braucht, dann steht wieder alles und wartet ....

    Nö, wieso? Du sollst deine Funktion doch dort nicht aufrufen (bzw. den Thread starten). Das machst du nach wie vor in Main_WndProc, wenn du die entsprechende Nachricht erhälst. In "do_something" sollst du nur deine Anzeige aktualisieren. Dafür muss die Funktion natürlich nicht-blockierend sein. Und wie sowas aussieht, hab ich bereits in meinem ersten Beitrag geschrieben.

    RHBaum schrieb:

    Klingt komplizierter als es ist ....

    Nö, klingt genauso kompliziert wie es ist. So kompliziert klingt es zwar gar nicht, 2 Threads sind aber trotzdem etwas zu dick aufgetragen, imo.


Anmelden zum Antworten