从辅助线程调用UpdateWindow()

我有一个Windows应用程序来显示非常复杂的vectorgraphics。 由于绘图需要一段时间才能完成,因此我将绘制逻辑移至单独的线程。

相关的代码片段如下。 这里CCanvas是从CWnd派生的, m_MemDC是CDC指针来绘制所有的graphics。 OnPaint()处理程序将此memdc内容清除到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 ); } 

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

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

注意:你将需要确保你用于后台渲染的机制是正确同步的:例如使用一个mutex来访问你的m_MemDC以避免后台线程尝试更新它,而前台线程正在读取它来绘制UI 。

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

AFAIK(我记得),Windows UI不保证是线程安全的。 从非线程处理事件循环的线程调用任何UI函数已知会引起麻烦。

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