如何在Delphi中以各种方式选择线程?

时间:2010-08-05 23:28:46

标签: multithreading delphi asynchronous

似乎我终于要在我的Delphi 2009程序中实现某种线程。如果只有一种方法可以做到这一点,我就会开始跑步。但我看到了几种可能性。

任何人都可以解释这些之间的差异以及为什么我选择一个而不是另一个。

  1. Delphi中的TThread类

  2. AsyncCalls Andreas Hausladen

  3. OmniThreadLibrary Primoz Gabrijelcic (gabr)

  4. ......还有其他人吗?


  5. 编辑:

    我刚刚阅读了Gabr在2010年3月(第10期)Blaise Pascal Magazine期刊中发表的一篇精彩文章,题为“四种创造线索的方法”。你必须订阅获取杂志的内容,所以根据版权,我不能在这里重现任何实质性的内容。

    总之,Gabr描述了使用TThreads,直接Windows API调用,Andy的AsyncCalls和他自己的OmniThreadLibrary之间的区别。他最后得出的结论是:

      

    “我不是说你必须选择除经典的德尔福方式之外的任何其他方式(TThread),但要了解你所拥有的选项仍然很好”

    Mghie的答案非常透彻,暗示OmniThreadLibrary可能更可取。但是我仍然对每个人关于我(或任何人)应该如何为他们的应用选择他们的线程方法的意见感兴趣。

    您可以添加到列表中:

    。 4.直接调用Windows API

    。 5. Misha Charrett's CSI Distributed Application Framework正如LachlanG在回答中所建议的那样。


    结论:

    我可能会选择OmniThreadLibrary。我喜欢加布尔的作品。我多年前使用过他的探查器GPProfile,而我现在正在使用他的GPStringHash,它实际上是OTL的一部分。

    一旦Embarcadero将该功能添加到Delphi中,我唯一关心的可能就是升级它以使用64位或Unix / Mac处理。

5 个答案:

答案 0 :(得分:43)

如果您没有多线程经验,那么您可能不应该从TThread开始,因为它只是本机线程上的一个薄层。我认为它的边缘也有点粗糙;自从Delphi 2引入以来,它并没有发展很多,主要是为了在Kylix时间框架内实现Linux兼容性,并纠正更明显的缺陷(比如修复损坏的MREW类,最后弃用Suspend()和最新Delphi版本中的Resume()

使用简单的线程包装器类基本上也会导致开发人员专注于太低的级别。为了正确使用多个CPU内核,关注任务而不是线程更好,因为使用线程分配工作并不能很好地适应不断变化的需求和环境 - 取决于硬件和其他并行运行的软件的最佳数量即使在同一系统的不同时间,线程也可能有很大差异。只传递大量工作的库,以及自动安排它们以充分利用可用资源的库在这方面有很大帮助。

AsyncCalls是将线程引入应用程序的第一步。如果程序中有几个区域需要执行多个彼此独立的耗时步骤,那么您可以通过将每个区域传递给AsyncCalls来异步执行它们。即使您只有一个这样耗时的操作,您也可以异步执行它,只需在VCL线程中显示进度UI,可选择允许取消操作。

AsyncCalls对于在整个程序运行期间保持不变的后台工作人员而言不太适合IMO,并且当程序中的某些对象具有线程关联性时可能无法使用(如数据库连接或可能具有要求所有调用都发生在同一个线程中。)

您还需要注意的是,这些异步操作的“即发即忘”类型。每个重载的AsyncCall()函数都会返回一个IAsyncCall接口指针,如果要避免阻塞,可能需要保留引用。如果你没有保留引用,那么当ref计数达到零时,接口将被释放,这将导致线程释放接口以等待异步调用完成。这是您在调试时可能会看到的内容,退出创建IAsyncCall的方法可能需要花费大量时间。

OTL在我看来是你三个选项中最通用的,我会毫不犹豫地使用它。它可以完成TThread和AsyncCalls可以做的所有事情,还有更多。它具有完善的高级设计,既可以让用户轻松生活,也可以让端口连接到Unixy系统(同时保持大部分接口完好无损),即使不容易也是如此。在过去的几个月里,它也开始为并行工作获得一些高级构造,强​​烈推荐。

OTL也有几十个样本,这对于入门非常重要。 AsyncCalls在评论中只有几行,但由于其功能有限,它很容易理解(它只做一件事,但它做得很好)。 TThread只有一个样本,在14年内没有真正改变,主要是如何不做事情的一个例子。

无论您选择哪种选项,任何库都不会消除理解线程基础知识的需要。读完一本关于这些的好书是任何成功编码的先决条件。例如,正确锁定是所有这些的必要条件。

答案 1 :(得分:7)

还有另一个鲜为人知的Delphi线程库,Misha Charrett的CSI Application Framework

它基于消息传递而不是共享内存。相同的消息传递机制用于在同一进程或其他进程中运行的线程之间进行通信,因此它既是线程库又是分布式进程间通信库。

有一些学习曲线可以开始,但是一旦你开始,你不必担心所有传统的线程问题,如死锁和同步,框架会为你处理大部分问题。

Misha多年来一直在开发这个产品,并且一直在积极改进框架和文档。他总是对支持问题非常敏感。

答案 2 :(得分:6)

TThread是一个封装Windows线程的简单类。您使用Execute方法创建一个子类,该方法包含此线程应执行的代码,创建线程并将其设置为运行并执行代码。

AsyncCalls和OmniThreadLibrary都是在线程之上构建更高级别概念的库。它们是关于任务,是您需要异步执行的离散工作。你启动库,它设置一个任务池,一组特殊线程,其工作是等待你为它们工作,然后你传递一个包含代码的函数指针(或方法指针或匿名方法)需要执行,并在一个任务池线程中执行它,并为您处理很多低级细节。

我没有使用任何一个库,所以我不能真正给你两者之间的比较。尝试一下,看看他们能做些什么,哪一个对你感觉更好。

答案 3 :(得分:6)

(对不起,我没有足够的评论点,所以我把它作为一个答案,而不是另一个投票给OTL)

我使用过TThread,CSI和OmniThread(OTL)。这两个库都有非平凡的学习曲线,但比TThread更强大。我的结论是,如果你要做任何有关线程的重要事情,你最终还是会编写一半的库函数,所以你不妨从别人写的工作调试版本开始。 Misha和Gabr都是比我们大多数人更好的程序员,所以他们做得比我们做得更好。

我看过AsyncCalls,但它没有做我想要的。它确实有一个“同步”功能(从OTL中丢失),所以如果你依赖于它,你可能会完全依靠AynscCalls。 IMO使用消息传递并不足以证明Synchronize的肮脏,因此请关闭并学习如何使用消息。

在我喜欢OTL的三个中,主要是因为收集了例子,但也因为它更加独立。如果你已经在使用JCL或者你只在一个地方工作,这不是一个问题,但是我做混合包括合同工作和销售客户安装Misha的系统比OTL更难,因为OTL是~20个文件在一个目录中。这听起来很愚蠢,但对很多人来说很重要。

使用OTL搜索关键字的示例和源代码,并在论坛中提问的组合对我有用。我熟悉传统的“卸载CPU密集型任务”线程作业,但是现在我正在研究一堆数据库工作的背景,这些工作有更多的“线程阻塞等待DB”而更少“CPU最大化”, OTL对此非常有效。主要区别在于我可以运行30多个线程而不会占用CPU最大值,但通常不可能停止运行。

答案 4 :(得分:1)

我知道这不是最先进的方法:-)也许它也有局限性,但我只是尝试了System。 BeginThread 并发现它非常简单 - 可能是因为它的质量文档我指的是...... http://www.delphibasics.co.uk/RTL.asp?Name=BeginThread(IMO Neil Moffatt可以教MSDN一两件事)

这是我在尝试学习新东西时发现的最大因素,即文档的质量,而不是数量。花了几个小时,然后我回到了真正的工作而不是担心如何让线程去做它的业务。

编辑实际上Rob Kennedy在这里解释BeginThread做得很好BeginThread Structure - Delphi

编辑实际上Rob Kennedy在同一篇文章中解释TThread的方式,我想我将改变我的代码以明天使用TThread。谁知道下周会是什么样子! (可能是AsyncCalls)