德尔福7和事件

问题描述:

我正在研究一个看起来像某种时间问题的错误,所以我对Delphi 7中事件的工作方式有点好奇。然后我们通过一个函数发送一些数据给我们的应用程序COM接口,并在COM线程引发的事件中处理它。看起来事件中有很多代码,执行需要的时间越来越长,过了一段时间后整个应用程序崩溃了。在图形和事件内部的大数组中存在可能影响时间的调用。我一直无法发现任何显着的内存使用量增加,并没有机会运行任何分析器来检查泄漏。另外,要测试的显而易见的事情是将其中所有代码的事件剥离,以查看我们是否可以运行更长的时间。德尔福7和事件

Delphi中的事件是串行或并行的,也就是说,如果我在执行一个新事件时发生了什么 - 发生了什么?它是否在某种自动线程中并行运行,是忽略还是排队?

如果排队等候,应用程序崩溃之前我可以在队列中拥有多少个队列?

索引到一个大阵列需要更长的时间进一步进入它你是?即使它的大小是固定的?我认为这不应该让我寻找需要时间的泄漏和分配。如果我通过事件发送了一个对象,我应该在事件内还是在“调用”代码中处理它?

什么东西通常在Delphi中不能很好地扩展?我能找到什么会增加执行时间?

最后,由于这是COM相关的,任何指向COM常见陷阱的指针都会被赞赏,尽管我意识到这很棘手。尽管如此,我确实掌握了共同初始化。

做了一些研究,并得到了几个三分球,特别是我的第一个问题:

事件是在Delphi串行或parallell,也就是说,如果我当一个新的事件正在执行 - 什么情况?它是否在某种自动线程中并行运行,是忽略还是排队? 如果排队,在应用程序崩溃之前,我可以在队列中拥有多少个队列?

好吧,显然事件与其他事物一样是同步的,或者我应该说是连续的。由于该事件本质上是一个函数调用,因此在处理一个事件时无法获得更多事件。

在事件处理程序中,会处理一些图形组件。由于该事件在另一个线程中引发,因此这是不好的。我需要对创建图形的线程上的图形或者在事件中进行线程切换的图形进行更新。

另外,测试表明它实际上是图形的更新花费的时间越来越长,因此重构图形处理听起来像是一个很好的尝试的路径。

德尔福主要是处理事件的串行。不幸的是,有可能告诉德尔福在处理某些事件时处理其他事件。因此,当您的当前事件正在等待新事件完成时,新事件将运行。在最糟糕的情况下,您的应用程序可能看起来行为正常,而事件内事件内的事件实际上是在堆叠事件。规则一:避免使用Application.ProcessMessages,除非你真的需要这个。

当使用COM对象,事情变得更加复杂一点,因为COM对象可能有它自己的事件,开始它自己的主题,做所有其他类型的东西,你没有任何控制权。 COM似乎很容易在Delphi中使用,但对于缺乏经验的开发人员来说,它确实存在许多隐藏的缺陷。 (我在幸存其中一些人后仍然留下疤痕!)

一般来说,当使用COM对象时,我尝试在自己的线程中分离COM调用,创建特殊组件,将COM对象保留在其中自己的线程并添加大量的同步代码,这样我可以保持图形用户界面的响应,而一些长的COM任务正在做一些处理。但是这样做需要很多COM和多线程的经验。但基本上,围绕任何COM组件设计自定义包装器组件是一种很好的做法,只是为了保护COM类所需的资源。

德尔福最强大的弱点往往是字符串处理和巨大数组的处理。 (尤其是包含对象的数组;使用记录代替。)Delphi中的字符串本身很快,但Delphi中的字符串函数并不是非常优化的。例如,我曾经有一个包含一些XML数据的字符串。它有许多布尔字段拼写为“True”和“False”,需要将它们转换为“true”和“false”。一个简单的字符串替换大约需要15秒来替换所有这些值。我通过使用MSXML将XML加载到DOM文档中,使用XPath选择所有布尔节点,通过这些节点循环来将所有值替换为正确的文本,然后将XML放回到单个字符串中,从而重写了它。突然之间,它能够在两秒钟内做到这一点!对于那些看起来比较慢的东西来说,这是一个巨大的表现。 原因?当Delphi处理字符串时,往往会在处理过程中复制字符串几次。或者它需要分配越来越多的内存来增加字符串的大小。这需要时间,这不会浪费在像C++这样的其他语言中。

+0

我完全不同意你的最后一段。天真==慢字符串处理可以在每种语言中实现,当然也可以在C++中实现。用一个适当的算法在字符串中将True和False置为小写,应该可以处理每秒几兆字节。如果(取决于字符串长度)10秒或100毫秒是可能的,从15秒到2秒?我不知道... – mghie 2009-06-08 19:06:49

COM的重要之处在于你的应用程序支持的“公寓模型”。在Delphi中最常用的是Single Threaded Apartment model也被称为“公寓线程”,其中COM调用与您的应用程序主消息队列同步。使用这个模型,你一次只能处理一个COM调用,COM对象不支持来自其他线程的调用。通过调用CoInitializeEx(nil, COINIT_MULTITHREADED);

每次启动线程将不得不加入多线程公寓 -

但是,您可以初始化COM单元模型是multithreaded并在这样做,你将不得不作出对共享资源确保获得正确同步

当您公开DCOM接口时,事情会变得非常有趣,因为RPC子系统有一个线程池用于处理请求,这些请求可以直接访问多线程单元中的所有COM对象,从而实现高性能服务器。如果您正在使用单元线程,则必须经历消息队列的瓶颈,并且单线程一次只能分派一个COM调用。

克里斯·本森写了nice blogpost这个也有一些代码示例。