演示在Delphi中使用线程的不同方法

太多的Delphi用户犯了一个错误,即认为线程是某种魔术,可以改善其应用程序的性能。不幸的是,这远非如此。试图实现线程时#1的最大错误是使其直接访问应用程序的视觉控制。但是这些视觉控制只能在应用程序主线程的上下文中起作用。必须使用另一个线程来更新用户界面中的控件,必须非常仔细地计划和实现。在大多数情况下,这可能根本不是解决问题的正确解决方案。
简而言之,Delphi的VCL框架不是安全的。尽管有许多方法可以将线程集成到您的UI中,但没有单一的单一适合解决方案。它总是会根据您要完成的目标而有所不同。在大多数情况下,人们希望取得更好的性能(速度)。但这很少是通过使用线程来完成的。取而代之的是,将线程集成到用户界面中的最常见方案是在长期任务中保持UI响应。
为此,我们会想象一个只有一个按钮的简单应用程序,该应用程序在单击时从Internet下载文件。该应用程序已经具有一个用于整个UI的主线程。在Windows平台上,这意味着发送/接收Windows消息,绘制到控制画布,识别用户交互等。此线程本质上是一个巨大的环路,非常快速地旋转。对于此旋转线的每一次革命,执行了某些代码。
在单个螺纹环境中,此文件下载将阻止此循环旋转,直到下载完成。在此期间,此线程不再能够执行任何UI更新,检测用户点击或其他任何内容。这就是导致Windows在此类形式的标题中放置(不响应)的原因,因为就像它所说的那样,它没有响应。
这是其他线程进来的地方。它需要响应窗口。您可以将该文件下载放入另一个线程中,而不是通过此巨型文件下载来阻止主UI线程。这很简单,对吗?
您可能会问自己“我该如何监视进度?”或“完成后如何通知?”这意味着下载线程需要以某种方式与主线程进行交互。这正是混乱的出现。一个线程不能简单地干扰另一个线程,因为没有说明一个线程实际上是什么。现在有两个单独的循环,当您想更新UI时,UI线程可以在任何地方做任何事情。最重要的是,假设主UI线程是在将字符串编写到同一控制属性的过程中,您的另一个线程也想写入。现在,您有两个线程试图写入相同的内存地址,这可能会导致不可预测的问题。
通过同步。 Delphi的TThread类具有方法同步(),该类允许线程仅在实际期望发生这种情况的时候才能与Main UI线程进行交互。从另一个线程同步的代码实际上并未在该线程的上下文中运行 - 它总是在主UI线程的上下文中运行。这就是同步()的想法,就是在UI线程中执行代码。
因此,最后,您实际上并未使用线程中的VCL。取而代之的是,您的线程将信号发送到主线程,并且只有在准备好主线程时,它才能执行该代码。同时,您的辅助线程在等待主线程完成时被阻止。
然后是一个错误,认为大型UI操作在线程中会更好。假设您有一个列表,您想在这里填充数百万个物品。当然,这将需要时间,在此期间,您的申请将无法响应。再次。因此,只需将代码移至线程,对吗?
同样,必须从主线程和主线程进行任何UI交互。如果您需要执行冗长的计算,处理大量数据,等待远程资源的响应或其他任何既耗时又与UI直接相关的内容,则线程很有用。
这很难说。但是,在编写线程时,有一个共同的做法:将您的线程代码放在自己的单元中。该单元应与任何其他UI单元隔离。它甚至不应在其用途子句中具有任何相关单元。该线程甚至不知道如何使用它。它本质上应该是一个假人,其唯一目的是执行冗长的任务。当涉及到线程UI更新时,最好通过同步事件来完成。
正是听起来的样子。如前所述,这是一个同步的事件。事件只是指向过程的指针,您可以在线程启动之前分配给该过程。在线程内部,当您需要更新UI时,您将使用Synchronize()触发此事件。通过这种设计,该线程永远不必知道它甚至被UI使用。同时,您也无意中完成了抽象。线程变得可重复使用。您可以将其插入其他可能甚至没有用户界面的项目(例如Windows服务)。
这是一些有关VCL线程安全的相关资源的直接链接,以防您不想搜索...
该应用程序演示了Delphi中线程的用法。由于有许多不同的知识,因此出于不同的目的,它们分为不同的部分。每个主题至少具有1个表单单元(嵌入到选项卡表中),除用户界面外,至少有1个独立单元包含其功能。这是故意完成的,以表明应与任何UI隔离线程。
主要形式本身实际上没有任何逻辑。它所做的只是将表单嵌入到选项卡中。在FormCreate()事件处理程序中,它对EmbedForm()进行了许多调用,该调用为每个标签表实例化。
实际使用该应用程序非常简单。您只需导航到其中一个标签,每个标签都会有自己的说明。


显示如何在线程中从Internet下载文件。有一个单一的通用函数定义的DownloadFile()执行下载。 UI有3个按钮:
默认情况下,要下载的URL是由ThinkBroadband.com提供的测试文件,但是您可以使用任何想要的URL。您也可以选择将文件保存到的位置。这是一个非常简单的演示,因此需要根据您的需求调整本地文件名的文件名/扩展名 - 它不会自动为您下载的URL更改(如浏览器通常这样做)。

显示如何从执行冗长任务的线程中更新进度条。

在线程中使用数据库连接并将数据同步到UI线程中。

演示多个螺纹消耗大量CPU周期以加载测试您的处理器。