我有一个用于显示非常复杂的矢量图形的Windows应用程序。由于绘图需要一段时间才能完成,因此我将渲染逻辑移动到一个单独的线程中。
下面给出了相关的代码段。这里CCanvas派生自CWnd
,m_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 );
}
答案 0 :(得分:2)
不,在创建Window的线程以外的线程上调用GUI函数是不安全的。
我会创建一个自定义消息,以便在图像准备好时从后台线程发布。然后主线程可以在正常的消息循环中处理它以重绘窗口。
注意:您需要确保用于背景渲染的机制已正确同步:例如使用互斥锁访问m_MemDC
,以避免后台线程在前台线程正在读取它时绘制UI来更新它。
我实际上建议使用两个绘制缓冲区。一个被用作渲染目标,一个被WM_PAINT
处理程序读取。渲染完成后,渲染线程可以锁定互斥锁,交换缓冲区,解锁互斥锁并发布消息。 WM_PAINT
处理程序可以锁定互斥锁,从活动缓冲区复制到窗口,并解锁互斥锁。这意味着渲染线程只会阻塞消息处理线程交换“活动缓冲区”标记所花费的时间,而不是因为任何其他原因(例如窗口)调用WM_PAINT
而导致的整个渲染时间被覆盖/未覆盖,或调整大小,或其他什么)
答案 1 :(得分:0)
AFAIK(我记得),Windows UI不保证是线程安全的。从一个不是处理事件循环的线程的线程调用任何UI函数会导致问题。
正确的方法是将消息发布到主事件循环,或者使循环使用MessageWaitForMultipleEvents
而不是简单GetMessage
以允许后台线程将任何事件发送到前台线程。