从创建UI的同一线程更新VCL.为什么?

opc*_*0de 8 delphi multithreading thread-safety

我知道我必须调用Synchronize来从没有创建控件的线程更新vcl或者向窗口发送消息.

我经常听到这个词不是线程安全的,但是我找不到关于发生了什么的实际解释.

我知道应用程序可能会因访问冲突而崩溃,但我不知道为什么?

请介绍一下这个话题.

Rem*_*eau 12

VCL UI控件中线程不安全的最大原因之一是TWinControl.Handle属性getter.它不仅仅是控件的简单只读访问器HWND.HWND如果它还不存在,它也会创建它.如果一个工作线程Handle在不HWND存在时读取该属性,它会HWND在工作线程上下文中创建一个新的,这很糟糕,因为HWNDs与创建线程上下文相关联,这将使得拥有控件最多无法使用,因为Windows消息为控件不再通过主消息循环.但糟糕的是,如果主线程读取相同的Handle在同一时间的工作线程确实财产(例如,如果主线程动态地重新创建Handle为任意数量的原因),所以它们之间的线程上下文创建了一个竞争条件HWND是被分配为新的Handle,以及潜在的句柄泄漏潜力,如果两个线程最终创建新的HWNDs但只有一个可以保留而另一个被泄露.

线程不安全的另一个罪犯是VCL的MakeObjectInstance()函数,VCL在内部使用该函数将TWinControl.WndProc()非静态类方法指定为TWinControl.Handle窗口的消息过程,以及将任何TWndMethod对象方法指定为由其HWND创建的消息过程.AllocateHWnd()功能(TTimer例如使用). MakeObjectInstance()做了相当多的内存分配/缓存和twiddling内存内容,不受多线程的并发访问保护.

如果您可以确保Handle提前分配控件,并且如果您可以确保主线程Handle在工作线程运行时从不重新创建,那么可以安全地从工作线程向该控件发送消息而不使用Synchronize().但这是不可取的,工作者线程必须考虑太多因素.这就是为什么最好只在主线程中完成所有 UI访问.这就是VCL UI系统的使用方式.


Arn*_*hez 8

关于Windows中的GDI线程安全性,请参阅此参考文章.

它清楚地表明您可以从多个线程安全地访问句柄,不应该同时进行.您需要保护对GDI句柄的访问,例如使用关键部分.

请记住,与大多数Windows句柄一样,GDI处理是映射到(在较新的Windows下,64位兼容性)的内部结构的指针.像在多线程计算中一样,同时访问相同的内容可能是问题的根源,这些问题很难识别和修复.integerNativeUInt

VCL本身的UI部分从一开始就不是线程安全的,因为它依赖于非线程安全的Windows API.例如,如果您在一个线程中释放GDI对象(在另一个线程中仍然需要),您将面临潜在的GPF.

Embarcadero(此时)可以使VCL线程安全,通过关键部分序列化所有UI访问,但它可能增加了复杂性,并降低了整体性能.请注意,即使是Microsoft .Net平台(在WinFormsWPF中)也需要一个用于UI访问的专用线程AFAIK.

因此,要从多个线程刷新UI,您有几种模式:

  1. 使用Synchronize来自线程的调用;
  2. WM_USER从后台线程发送GDI自定义消息(请参阅参考资料)以通知UI线程需要刷新;
  3. 采用无状态方法:UI将不时刷新其内容,从逻辑层(使用计时器或按某些可能更改数据的按钮).

从我的角度来看,我更喜欢大多数UI的选项2,以及用于远程客户端 - 服务器访问的附加选项3(可以与选项2混合使用).因此,您不必从服务器端向UI触发某些更新事件.在HTTP/AJAX RESTful世界中,这确实很有意义.选项1有点慢,恕我直言.在所有情况下,选项2和3都需要一个清晰的n层分层架构,其中逻辑和UI不会混合:但无论如何,对于任何严肃的开发来说,这都是一个很好的模式.

  • "它清楚地表明你可以安全地从多个线程访问句柄,但它不应该同时进行." 它没有这么说.只要您的访问权限是只读的,那么多个线程就可以同时访问GDI,窗口属性等.当谈到线程安全时,它是无意义的,这是毫无意义的.正如Eric Lippert指出的那样,线程安全一词本身毫无意义:http://blogs.msdn.com/b/ericlippert/archive/2009/10/19/what-is-this-thing-you-call -thread-safe.aspx (2认同)
  • 你是对的,来自技术POV.我考虑到这样一个事实:访问GDI句柄主要是向他发送消息,并可能分配和释放相关资源(字体,笔......).我的建议/规则是通过阻止同时访问来确保访问是安全的,特别是通过VCL层.将消息发送到GDI实例*是*线程安全的,但是分配/释放GDI资源和设置参数不是 - 这是写的.您可以通过混合消息来混合GDI命令,并且它可能具有意外行为.所以"一次一个". (2认同)