c ++ - Calling UpdateWindow () fra en sekundær tråd

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg har et Windows-program til visning af meget kompleks vektorgrafik. Siden tegningen tager et stykke tid at afslutte, flyttede jeg renderingslogikken til en separat tråd.


Den relevante kodestykke er angivet nedenfor. Her CCanvas er afledt fra CWnd og m\_MemDC er CDC pointer for at tegne hele grafikken. OnPaint() handler bitblts dette memdc indhold til PaintDC.


I Render () -metoden skal jeg opdatere displayvinduet, når billedtrækningen er afsluttet. Her kalder jeg Invalidate() og UpdateWindow() metoderne direkte. Er det sikkert at kalde disse metoder fra en sekundær tråd?


void CCanvas::UpdateDisplay()
{
    ::SetEvent(m\_hRenderWaitEvent);
}

DWORD WINAPI RenderThread(LPVOID lpParam)
{
    CCanvas* pThis = static\_cast<CCanvas*>(lpParam);
    pThis->Render();

    return 0;
}

void CCanvas::Render()
{
    HANDLE hEvents[] = {m\_hStopEvent, m\_hRenderWaitEvent};
    while (true)
    {
        switch (WaitForMultipleObjects(2, hEvents, FALSE, INFINITE))
        {
        case WAIT\_OBJECT\_0 + 0:
            return;
        case WAIT\_OBJECT\_0 + 1:
            Draw(&m\_MemDC);
            Invalidate();
            UpdateWindow();
            break;
        }
    }
}

void CCanvas::Draw( CDC* pDC )
{
    //Image drawing logic here
}

void CCanvas::OnPaint()
{
    CPaintDC dc( this );

    CRect rctClient;
    GetClientRect( rctClient );
    dc.BitBlt( rctClient.left, rctClient.top, rctClient.Width(), rctClient.Height(), &m\_MemDC, rctClient.left, rctClient.top, SRCCOPY );
}

Bedste reference


Nej, det er ikke sikkert at kalde GUI-funktioner på en anden tråd end den tråd, der skabte vinduet.


Jeg ville oprette en brugerdefineret besked til at sende fra baggrundstråden, når billedet er klar. Hovedtråden kan så håndtere dette i den normale meddelelsessløjfe for at omdanne vinduet.


Bemærk: Du skal sørge for, at den mekanisme, du bruger til baggrundsgengivelsen, er korrekt synkroniseret: f.eks. brug en mutex omkring adgang til din m\_MemDC for at undgå at baggrundstråden forsøger at opdatere den, mens forgrundstråden læser den for at male brugergrænsefladen.


Jeg vil faktisk anbefale have to malingsbuffere. En, der bruges som renderingsmål, og en der læses af WM\_PAINT -handleren. Når gengivelsen er færdig, kan render-tråden låse mutex, bytte buffere, låse op mutex og sende meddelelsen. Handleren WM\_PAINT kan låse mutexen, kopiere fra den aktive buffer til vinduet og låse op mutexen. Det betyder, at render-tråden kun blokerer meddelelseshåndteringsgruppen for den tid det tager at bytte markøren 'aktiv buffer' i stedet for hele gengivelsestiden, hvis WM\_PAINT bliver kaldt af andre årsager (f.eks. Dit vindue er dækket/afdækket, eller ændret, eller hvad som helst)

Andre referencer 1


AFAIK (og jeg kan huske), Windows UI er ikke garanteret at være tråd sikker. Kaldning af en brugergrænsefladefunktion fra en tråd, der ikke er den tråd, der behandler hændelsesløkken, er kendt for at forårsage problemer.


Den rigtige måde er enten at sende en besked til hovedhændelsesløkken eller for at gøre sløjfen brug MessageWaitForMultipleEvents i stedet for den enkle GetMessage for at tillade baggrundstråder at sende nogen begivenhed til forgrundstråden.