Delphi Timage由另一个线程刷新

时间:2015-07-27 16:11:23

标签: multithreading delphi graphics

不确定这可能,可能不是,但我问。 我有一个Timage,我在里面使用Timage.canvas绘制形状 这是通过用户线程而不是主应用程序线程来实现的,因为主线程(不幸的是由于历史原因)处理另一个同步进程,使其停止处理消息。 我可以做image.lock / unlock,写入内部,但图像不刷新。似乎只有当mai的线程可以处理消息时才重新绘制。 我也不能使用同步方法。 可能只允许主应用程序线程刷新图形对象吗?

3 个答案:

答案 0 :(得分:2)

  

主要线程(遗憾的是由于历史原因)处理另一个同步进程,使其停止处理消息。

您需要更改该设计。历史与否,在任何明显的时间内阻止主UI线程是完全错误的。无论主线程同步执行什么,都需要重写该逻辑 - 如果它必须保留在主UI线程中(如果它依赖于具有线程关联的任何东西),则转换为异步进程,或者移动到单独的worker它可以阻塞的线程,然后在需要时通知主线程更新。

在您正确修复该设计之前,至少要查看是否可以更新同步过程以定期调用Application.ProcessMessages()。主线程需要能够及时处理OS消息,否则操作系统会将进程标记为无响应,灰色/模糊UI,用户可能会将其终止。

  

似乎只有当mai的线程可以处理消息时才重新绘制。

这是正确的。具体来说,当TImage.Parent控件处理WM_PAINT消息时,TImageTGraphicControl后代,因此它不直接从操作系统接收WM_PAINT消息,但是而是从其Parent控件中委派的。

  

我也不能使用同步方法。

一旦你修复了阻塞主线程的破坏设计,你就可以使用TThread.Synchronize()TThread.Queue()SendMessage()PostMessage()等。需要触摸UI的工作线程必须与主线程同步。

  

是否只允许主应用程序线程刷新图形对象?

是。您应该做的是将同步过程更改为异步过程,然后将绘制线程绘制到内存中TBitmap(您仍需要使用Canvas.Lock() / Unlock()方法保护共享的GDI资源),然后与主UI线程同步,在需要时将TBitmap绘制到TImage

如果您无法将同步过程更改为异步过程,那么至少要查看是否可以更新同步过程以定期检查绘图线程以获取更新的TBitmap,抓住它并将其绘制到{{ 1}},然后通过TImage方法强制立即重绘UI。

答案 1 :(得分:1)

根据WM_PAINT消息绘制图像。该消息被传递给主线程。您已阻止哪个。因此问题。

唯一的解决方案是修复破损的设计。不要阻止主线程。将长时间运行的任务移动到工作线程上,以便主线程可以抽取其消息队列,从而保持响应。

答案 2 :(得分:1)

Windows重新绘制是由Windows本身(GDI层)在向窗口的主线程发送适当的消息时进行的。

在这些特殊状态下,Windows会创建特殊的“设备上下文”(Paint DC),允许在屏幕上绘制窗口。那些特殊的PaintDCs负责剪裁(当窗口被另一个或菜单覆盖,或部分位于桌面之外时等)。 在这些消息之外(并且缺少Paint DC)处理重新绘制窗口将无法正常工作。

所以你必须考虑从长时间操作中卸载Main VCL Thread。

虽然有可能存在黑客攻击 - 就像你可以在外部线程中创建一个特殊窗口,它会漂浮在主窗口上方并显示图像。但是,您必须使用原始Windows GDI API创建此窗口并避免使用VCL。 好吧,Andy(我认为那是他)尝试采用VCL进行多线程工作并撰写了一篇名为Dialog Windows和Fibers的文章 - 但这是概念证明,而且非常有限且脆弱,不是一个实用的解决方案

这很可能比正常方式和卸载主线程更复杂,更容易出错。

另一个hack会在你的长任务中插入Application.ProcessMessages个调用 - 但这可能会造成肮脏且非常危险的练习 - 除非你对不同的消息保持非常严格和细粒度的控制 - 无限递归或某些其他意想不到的事件组合。同样,这是一种危险且不可靠的方法。