使用Delphi掌握多线程需要哪些核心原则?

时间:2011-11-17 07:41:02

标签: multithreading delphi

我对编程很新(在Delphi中打开和关闭大约8个月,在这里和那里有一点点Python),我正在购买一些书籍。

我有兴趣学习并发编程和使用Delphi构建多线程应用程序。每当我搜索“多线程Delphi”或“Delphi多线程教程”时,我似乎都会得到相互矛盾的结果,因为有些东西是关于使用某些库(Omnithread库)而其他东西似乎更适合具有更多经验的程序员。

我已经研究了很多关于德尔福的书籍,并且在很大程度上它们似乎有点撇开表面并且没有真正深入研究这个主题。我有一个程序员(他使用c ++)的朋友,他建议我在使用线程时了解底层系统的实际情况,而不是首先在我的程序中跳转到如何实际实现它们。

在Amazon.com上有很多关于并发编程的书籍,但似乎没有一本书是用Delphi制作的。

基本上我需要知道在开始使用线程之前我应该​​专注于学习的主要内容,如果我能够/应该尝试使用并非专门针对Delphi开发人员的书籍来学习它们(不想现在用自己的其他语言中的一堆代码示例来混淆自己的阅读书籍。如果有关于这个主题的任何可靠的资源/书籍,这里的任何人都可以推荐。

4 个答案:

答案 0 :(得分:20)

简短回答
转到OmnyThreadLibrary安装它并阅读网站上的所有内容。

更长的回答
你问了一些信息,所以这里有:
这里有一些要阅读的内容:

http://delphi.about.com/od/kbthread/Threading_in_Delphi.htm
我个人喜欢:Multithreading - The Delphi Way.
(它已经很久了,但基础仍然适用)

基本原则:
您的基本VCL应用程序是单线程的 VCL没有考虑多线程构建,而是使用线程支持,以便大多数VCL组件线程安全。 这样做的方法是让CPU等待,所以如果你想要快速应用程序,请注意何时以及如何与VCL通信。

与VCL沟通
你的基本线程是TThread的后代,拥有自己的成员 这些是每个线程变量。只要您使用这些,就没有任何问题 我最喜欢的与主窗口通信的方式是使用自定义窗口消息和postmessage进行异步通信 如果您想要同步通信,则需要使用关键部分synchonize方法。
请参阅此文章,例如:http://edn.embarcadero.com/article/22411

在线程之间进行通信
这是事情变得棘手的地方,因为你可能遇到各种难以调试的同步问题 我的建议:使用OmnithreadLibrary,也请看这个问题:Cross thread communication in Delphi
有些人会告诉你,读取和写入整数在x86上是原子的,但这并非100%正确,所以不要以天真的方式使用它们,因为你很可能会得到错误的细微问题并最终难以调试代码。

启动和停止线程
在旧的Delphi版本中使用了Thread.suspendThread.resume,但不再推荐这些版本,应该避免(在线程同步的上下文中)
看到这个问题:With what delphi Code should I replace my calls to deprecated TThread method Suspend?
也看看这个问题,虽然答案更模糊:TThread.resume is deprecated in Delphi-2010 what should be used in place?
可以使用suspendresume来暂停和重新启动线程,只是不要将它们用于线程同步。

效果问题
在您的线程中放置wait_for...synchonize等代码会有效地阻止您的线程,直到它正在等待的操作发生。 在我看来,这打破了线程的一个重要目的:速度
因此,如果你想要快,你必须要有创意。

很久以前我写过一个名为Life32的应用程序 它是conways game of life的显示程序。这可以非常快速地生成模式(在小模式上每秒数百万代) 它使用单独的线程进行计算,并使用单独的线程进行显示 显示是一个非常慢的操作,不需要每一代都完成。
生成线程包括显示代码,从显示中删除内容(在视图中时),显示线程只是设置一个布尔值,告诉生成线程也显示添加的内容。
生成代码使用DirectX直接写入视频内存,无需VCL或Windows调用,也无任何同步 如果移动主窗口,应用程序将继续显示在旧位置,直到您暂停生成,从而停止生成线程,此时安全更新线程变量。
如果线程没有100%同步,显示发生的时间太晚,没什么大不了的 它还具有自定义内存管理器,可避免标准内存管理器中的线程安全缓慢。 通过避免任何和所有形式的线程同步,我能够消除90%+(在小模式上)到0的开销。

答案 1 :(得分:15)

你真的不应该让我开始这个,但无论如何,我的建议:

  • 尽量不要使用以下内容:

    • TThread.Synchronize
    • TThread.WaitFor
    • TThread.OnTerminate
    • TThread.Suspend
    • TThread.Resume,(在某些Delphi版本的构造函数末尾除外)
    • TApplication.ProcessMessages
  • 使用PostMessage API与主线程进行通信 - 发布lParam中的对象,比如说。

  • 使用生产者 - 消费者队列与辅助线程进行通信,(不是Windows消息队列 - 只有一个线程可以在WMQ上等待,从而无法进行线程池化。)
  • 不要直接从一个线程写入另一个线程中的字段 - 使用消息传递。
  • 确实非常努力地在应用程序启动时创建线程,并且根本没有明确地终止它们。
  • 使用对象池而不是不断创建和释放对象以进行线程间通信。

结果将是一个表现良好,不会泄漏,不会死锁并在关闭主表单时立即关闭的应用程序。

Delphi应该内置的内容:

  1. TWinControl.PostObject(anObject:TObject)TWinControl.OnObjectRx(anObject:TObject) - 从辅助线程发布对象并使用它们触发主线程事件的方法。一个简单的PostMessage换行来替换表现不佳,死锁的,不断重写的TThread.Synchronize

  2. 一个简单的,无限制的生产者 - 消费者类,实际上适用于多个生产者/消费者。这就像20行TObjectQueue后裔,但Borland / Embarcadero无法管理它。如果您有对象池,则不需要复杂的有界队列。

  3. 一个简单的线程安全的阻塞对象池类 - 再次,Delphi非常简单,因为它有类变量和虚拟构造函数,例如。创建大量缓冲对象:
    myPool:=TobjectPool.create(1024,TmyBuffer);

答案 2 :(得分:5)

我认为实际上尝试编译一个应该了解多线程的事情列表可能是有用的。

  • 同步原语:mutexessemaphores,监控
  • 同步原语的Delphi实现:TCriticalSectionTMREWSyncTEvent
  • 原子操作:关于什么操作是原子操作和什么不操作的一些知识(讨论in this question
  • Windows API多线程功能:InterlockedIncrementInterlockedExchange,...
  • OmniThreadLibrary

当然,这远未完成。我制作了这个社区维基,以便每个人都可以编辑。

答案 3 :(得分:2)

附加所有其他答案我强烈建议您阅读以下书籍: “现代操作系统”或任何其他进入多线程细节的人。

这似乎是一种矫枉过正,但它会让你成为更好的程序员 你明确地获得了很好的洞察力 以抽象的方式进入线程/进程 - 所以你要了解为什么以及如何进行 在例子中使用关键部分或信号量(如 用餐哲学家问题或沉睡的理发师问题)