使用Delphi掌握多线程所需的一些核心原则是什么?



我是一个编程新手(大约8个月,断断续续使用Delphi和一点Python),我正在购买一些书籍。

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

我已经研究了相当多关于Delphi的书,在大多数情况下,他们似乎有点肤浅,而不是真正深入这个主题。我有一个程序员朋友(他使用c++),他建议我在使用线程时了解底层系统实际发生了什么,而不是首先跳到如何在我的程序中实际实现它们。

在Amazon.com上有相当多关于并发编程的书,但似乎没有一本是针对Delphi编写的。

基本上我需要知道什么是主要的事情,我应该专注于学习之前跳入使用线程,如果我可以/应该尝试学习他们使用的书籍不是专门针对Delphi开发人员(不想混淆自己阅读的书籍与其他语言的一堆代码示例现在),如果有任何可靠的资源/书籍的主题,这里的任何人都可以推荐。

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

长回答
你问了一些信息,所以这里是:
以下是一些可供阅读的内容:

http://delphi.about.com/od/kbthread/Threading_in_Delphi.htm
我个人喜欢:多线程- Delphi方式。

基本原则:
基本的VCL应用程序是单线程的。
VCL在构建时并没有考虑到多线程,相反,线程支持是固定的,因此大多数VCL组件都是而不是线程安全的。实现这一点的方法是让CPU等待,因此,如果您想要一个快速的应用程序,请注意何时以及如何与VCL通信。

与VCL通信
你的基本线程是TThread的从属线程,它有自己的成员。
这些是每个线程的变量。只要你用这些就不会有任何问题。
我最喜欢的与主窗口通信的方式是使用自定义窗口消息和postmessage异步通信。
如果你想要同步通信,你需要使用临界区synchonize方法。参见本文示例:http://edn.embarcadero.com/article/22411

线程间通信
这就是事情变得棘手的地方,因为您可能会遇到各种难以调试的同步问题。
我的建议:使用OmnithreadLibrary,也可以看到这个问题:Delphi中的交叉线程通信
有些人会告诉您,在x86上读写整数是原子性的,但这并不是100%正确的,所以不要天真地使用这些方法,因为您很可能会遇到一些微妙的问题,并最终得到难以调试的代码。

启动和停止线程
在旧的Delphi版本中,Thread.suspendThread.resume被使用,但是这些不再被推荐并且应该避免(在线程同步的上下文中)。看看这个问题:我应该用什么delphi代码来替换我对已弃用的TThread方法Suspend的调用?
也看看这个问题,虽然答案更模糊:TThread。resume在Delphi-2010中已弃用,应该使用什么?
可以使用suspendresume来暂停和重启线程,只是不要将它们用于线程同步。

性能问题
wait_for..., synchonize等代码在你的线程有效地停止你的线程,直到它等待的动作已经发生。
在我看来,这违背了线程的一个重要目的:速度
所以如果你想要快,你就必须要有创意。

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

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

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

    • TThread.Synchronize
    • TThread.WaitFor
    • TThread.OnTerminate
    • TThread.Suspend
    • TThread.Resume,(除了在某些Delphi版本的构造函数末尾)
    • TApplication.ProcessMessages
  • 使用PostMessage API与主线程通信- lParam中的post对象,例如

  • 使用生产者-消费者队列与次要线程通信,(不是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);

我认为编写一个关于多线程应该知道的事情的列表可能会很有用。

    同步原语:互斥,信号量,监视器
  • Delphi实现同步原语:TCriticalSection, TMREWSync, TEvent
  • 原子操作:关于哪些操作是原子操作,哪些不是原子操作的一些知识(在这个问题中讨论)
  • Windows API多线程功能:InterlockedIncrement, InterlockedExchange,…
  • OmniThreadLibrary

当然这还远远没有完成。我创建这个社区wiki是为了让每个人都可以编辑。

在回答以上问题的同时,我强烈建议你阅读以下书籍:"现代操作系统"或任何其他多线程细节。

这似乎是一个过度的,但它会使你成为一个更好的程序员和你肯定会得到一个很好的洞察力以一种抽象的方式进入线程/进程-这样你就可以了解为什么和如何在示例中使用临界区或信号量(如吃饭的哲学家问题还是睡觉的理发师问题)

最新更新