从辅助线程调用UpdateWindow()

时间:2017-03-20 05:31:26

标签: c++ windows multithreading winapi mfc

我有一个用于显示非常复杂的矢量图形的Windows应用程序。由于绘图需要一段时间才能完成,因此我将渲染逻辑移动到一个单独的线程中。

下面给出了相关的代码段。这里CCanvas派生自CWndm_MemDC是CDC指针,用于绘制所有图形。 OnPaint()处理程序将此memdc内容bitblts到PaintDC。

在Render()方法中,一旦完成图像绘制,我必须更新显示窗口。在这里,我直接调用Invalidate()UpdateWindow()方法。从辅助线程调用这些方法是否安全?

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 );
}

2 个答案:

答案 0 :(得分:2)

不,在创建Window的线程以外的线程上调用GUI函数是不安全的。

我会创建一个自定义消息,以便在图像准备好时从后台线程发布。然后主线程可以在正常的消息循环中处理它以重绘窗口。

注意:您需要确保用于背景渲染的机制已正确同步:例如使用互斥锁访问m_MemDC,以避免后台线程在前台线程正在读取它时绘制UI来更新它。

我实际上建议使用两个绘制缓冲区。一个被用作渲染目标,一个被WM_PAINT处理程序读取。渲染完成后,渲染线程可以锁定互斥锁,交换缓冲区,解锁互斥锁并发布消息。 WM_PAINT处理程序可以锁定互斥锁,从活动缓冲区复制到窗口,并解锁互斥锁。这意味着渲染线程只会阻塞消息处理线程交换“活动缓冲区”标记所花费的时间,而不是因为任何其他原因(例如窗口)调用WM_PAINT而导致的整个渲染时间被覆盖/未覆盖,或调整大小,或其他什么)

答案 1 :(得分:0)

AFAIK(我记得),Windows UI不保证是线程安全的。从一个不是处理事件循环的线程的线程调用任何UI函数会导致问题。

正确的方法是将消息发布到主事件循环,或者使循环使用MessageWaitForMultipleEvents而不是简单GetMessage以允许后台线程将任何事件发送到前台线程。