【问题标题】:Drawing to window from child thread从子线程绘制到窗口
【发布时间】:2017-06-21 02:18:02
【问题描述】:

我的应用程序从工作线程中绘制图形已有 10 多年了,我从未遇到过任何问题。工作线程像这样绘制到我的HWND(由主线程创建):

hdc = GetDC(hwnd);
SetDIBitsToDevice() ... or StretchDIBits()
ReleaseDC(hwnd, hdc);

在将我的应用程序移植到其他平台后,我开始意识到,在许多平台(例如 macOS)上,从主线程以外的任何其他线程进行绘图通常是不可行的。我的研究表明,这对于 Win32 也可能是正确的,但我仍然缺乏明确的答案。

因此,我的问题:

是否允许从没有创建它正在绘制到的窗口的工作线程中绘制到我的窗口,如上所示?请注意,工作线程实际上是唯一绘制到窗口的线程。主线程不做任何绘图。甚至在WM_PAINT 中也没有。在我的情况下,绘制WM_PAINT 是不必要的,因为工作线程以 50fps 的速度绘制。

如果不允许,将绘图从工作线程委托给主线程的最佳方式是什么?

【问题讨论】:

  • 这在 Win32 上绝对是错误的,而且一直都是。如果它可靠地工作(我怀疑它没有,错误很难重现),这是一个意外。所有绘图都应响应WM_PAINT,期间完成。为什么应用程序需要以 50 fps 的速度进行绘制?如果这是一款真正需要 50 fps 的游戏或其他类型的花哨业务,您可能应该使用 DirectX 或其他什么。
  • 无论如何,如果您必须跨多个线程执行此操作:让您的工作线程准备 DIB 部分,然后通过调用 RedrawWindow 强制窗口立即重绘自身。然后主线程将收到WM_PAINT 消息,在该消息处理程序中,您将 DIB 部分与消息一起绘制到您收到的 DC 中。同样,尚不清楚在这里使用多线程会给您带来什么,但这将使它起作用。
  • @CodyGray:但 MSDN 明确表示允许在 WM_PAINT 之外绘图。见这里:msdn.microsoft.com/de-de/library/windows/desktop/…
  • 我不确定它是否像@CodyGray 建议的那样相当错误,但这不是“最佳实践”。同步工作线程和 UI 线程的绘制通常会出现问题,但如果 UI 线程自己不绘制它可能是安全的。 (请注意,您仍然需要回复WM_PAINT,即使您没有为它做任何绘图)。另一种方法是在工作线程上创建一个子窗口。

标签: multithreading winapi gdi


【解决方案1】:

是否允许从未创建要绘制到的窗口的工作线程中绘制到我的窗口,如上所示?

这可能不是解决您问题的最佳方法,但它是安全的,只要您遵守 GetDC 的记录规则:

  • 请注意,DC 的句柄在任何时候都只能由单个线程使用。
  • ReleaseDC 必须从调用 GetDC 的同一线程中调用。

如果您确实从多个线程渲染到相同的设备上下文,则您负责同步对它的访问。*

如 cmets 中所述,更好的解决方案是从工作线程生成 DIB,并让该线程通过调用 RedrawWindow 更新窗口。然后主线程可以在其WM_PAINT 处理程序中StretchBlt。跨线程调用RedrawWindow 实现了同步屏障。当调用返回时,目标线程上的渲染已经运行完成,可以安全地重用 DIB。


*Thread affinity of user interface objects, part 2: Device contexts

【讨论】:

    猜你喜欢
    • 2017-06-13
    • 2022-01-02
    • 2013-01-28
    • 1970-01-01
    • 2010-10-05
    • 1970-01-01
    • 1970-01-01
    • 2010-10-01
    • 1970-01-01
    相关资源
    最近更新 更多