Invalidate() aus Thread heraus aufrufen



  • Tag zusammen,

    ich führe in einem Thread Berechnungen aus und möchte diese live auch gleich graphisch darstellen. Die Darstellung wollte ich in der Methode CView::OnDraw(CDC* pDC) ausführen. Meine Vermutung war, dass ich diese Methode z.B. mit Invalidate() ausführen kann. Daher folgender Code für die Thread-Funktion:

    DWORD WINAPI CVIView::ThreadFunc(LPVOID data)
    {
    	while(continueThread)
    	{
    		//Berechnung ausführen
    		Invalidate();
    		Sleep(100);
    	}
    	return(0);
    }
    

    Ich erhalte die Fehlermeldung

    error C2352: 'CWnd::Invalidate::CWnd::Invalidate': Unzulässiger Aufruf einer nicht statischen Memberfunktion
    

    Ich verstehe das so, dass die Thread-Funktion, da sie selbst statisch ist, nur statische Memberfunktionen aufrufen kann? Gibt es keinen Weg, das zu umgehen? Habe das Problem auch schon mit einer weiteren Funktion gehabt, aber da konnte ich mir anders helfen. Nur hier weiß ich keine Alternative 😞

    Wäre nett, wenn mir jemand weiterhelfen könnte :xmas1:



  • das geht nicht, und feddich
    loes es doch so das du aus dem thread heraus eine eigene message schickst zb
    bla->SendMessage(WM_UPDATEVIEW);
    und dann in der bla nur noch das Invalidate() aufrufen sobald diese message eingeht, das ist auch so die normale heran gehens weise, wobei deine ansicht alle 1 millisekunde geupdatet wird mit diesem code, koennte n bissl flackern #gg



  • Allgemein kann die Thread-Funktion durchaus nicht-statische Methoden aufrufen - sie hat (als statische Methode) nur keinen this-Zeiger, auf den sie zugreifen kann und muß sich deshalb das Objekt auf anderem Weg beschaffen (üblicherweise steckt es hinter dem LPVOID-Parameter:

    DWORD WINAPI CVIView::ThreadFunc(LPVOID data)
    {
        CVIView* pThis = (CVIView*) data;
        while(continueThread)
        {
            //Berechnung ausführen
            pThis->Invalidate();
            Sleep(100);
        }
        return(0);
    }
    

    ABER: MFC (oder war's die WinAPI?) bringt dir zur Laufzeit Probleme, wenn Worker-Threads direkt auf die Fenster zugreifen wollen - das darf nur der Thread, der das Fenster auch erstellt hat. Darum ist die übliche Vorgehensweise:

    • schreib deine Ausgabedaten an eine Stelle, wo du sie später wiederfinden kannst (z.B. in Member von pThis)
    • schick eine WM_APP-Nachricht an dich selber - bei der Verarbeitung dieser Nachricht kannst du dann deine Daten anzeigen lassen


  • OK, danke erstmal für die Hilfe. Allerdings tut sich mir da noch eine Frage auf, da ich in der ganzen MFC-Geschichte noch absoluter Neuling bin: wie schicke ich eine Nachricht an mich selbst? Ich habe es mit

    SendMessage(WM_NACHRICHT);
    

    versucht, aber das scheint auch wieder eine nicht-statische Funktion zu sein, meint der Compiler.


  • Mod

    Workerthreads können auf Fenster zugreifen, hier ist ein Invalidate kein Problem.
    Man sollte nur niemals ein CWnd* Objekt über Threadgrenzen weitergeben sondern nur HWND handles.

    Auch sollte man sich klar sein, was InvalidateRect und UpdateWindow auslösen.

    Ein InvalidateRect ist IMHO gänzlich ungefährlich aus einem Workerthread. Wenn der GUI Thread an der Reihe ist, wird er das Fenster neu zeichnen.
    Allerdings musst Du die Daten Threadsicher einpacken, nicht das es zu Problemen kommt.

    UpdateWindow/RedrawWindow ist problamtisch aus dem Workerthread. In diesem Fall wird eine threadsynchronisation erzwungen und der Workerthread wartet bis das Fenster gezeicnet wurde und das geschieht auch nur wenn der GUI Thread irgendwann GetMessage/PeekMessage ausführt.

    Man sollte solche Threadsynchronisationen vermeiden, weil sie die Arbeit immens ausbremsen.
    Was MrEvil mit SendMessage vorschlug ist deshalb auch problematisch. SendMessage kehrt erst zurück, wenn der UI Thread die Nachricht abgearbeitet hat.



  • Also ich habe jetzt die Variante von CStoll getestet, die funktioniert ohne Fehler und erfüllt ihren Zweck. Danke nochmal. Ich wollte aber auch die SendMessage-Variante probieren und hätte da gleich noch eine Frage:

    Ich habe folgendes getan:

    DWORD WINAPI CVIView::ThreadFunc(LPVOID data)
    {
        CVIView* pThis = (CVIView*) data;
        while(continueThread)
        {
            //Berechnung ausführen
            pThis->SendMessage(WM_UPDATEVIEW);
            Sleep(100);
        }
        return(0);
    }
    

    Die Nachricht registrieren:

    #define WM_UPDATEVIEW (WM_USER + 1)
    

    Ich habe die Definition ins Headerfile der Klasse geschrieben (da gehört sie doch hin oder?). Nun dachte ich, sie würde im VS dort mit auftauchen, wo man die Handler der verschiedenen Nachrichten festlegen kann. Tut sie aber nicht. Hab ich was falsch gemacht, oder wie kann ich die Nachricht jetzt verarbeiten?



  • Du hast nichts falsch gemacht - VC behandelt diese Nutzerdefinierten Nachrichten-Codes nur etwas stiefmütterlich (soll heißen, du mußt die Handler-Methode von Hand anlegen und in die Message-Map eintragen).



  • Alles klar, besten Dank. Dann sollten erstmal alle Fragen geklärt sein :xmas1:


Anmelden zum Antworten