如何在Delphi中进行异步编程?

时间:2012-09-14 12:50:48

标签: multithreading delphi asynchronous delphi-xe

我有一个应用程序,其中大多数操作需要一些时间,我希望始终保持GUI响应。用户触发的任何操作的基本模式如下:

  1. 准备动作(在主线程中)
  2. 执行操作(在后台线程中保持gui响应)
  3. 显示结果(在主线程中)
  4. 我尝试了几件事来实现这一目标,但从长远来看,所有这些都会导致问题(在某些情况下看似随机访问违规)。

    1. 准备操作,然后调用后台线程,在后台线程结束时,使用Synchronize在主线程中调用OnFinish事件。
    2. 准备操作,然后调用后台线程,在后台线程结束时,使用PostMessage通知GUI线程结果已准备好。
    3. 准备操作,然后调用后台线程,然后忙等待(同时调用Application.ProcessMessages)直到后台线程完成,然后继续显示结果。
    4. 我无法提出另一种选择,但这对我来说都不是很完美。这样做的首选方法是什么?

3 个答案:

答案 0 :(得分:5)

您可以使用OTL作者here

所展示的OTL来实施质疑模式

答案 1 :(得分:5)

1)采用'Orignal Delphi'方式,强制后台线程等待同步方法执行,并使系统暴露于比我满意的更多死锁潜力。 TThread.Synchronize至少重写了两次。我曾经在D3上使用过一次,并且遇到了问题。我看看它是如何工作的。我再也没用过它。

2)我经常使用的设计。我使用app-lifetime线程(或线程池),创建线程间通信对象,并使用基于TObjectQueue后代的生产者 - 消费者队列将它们排队到后台线程。后台线程对对象的数据/方法进行操作,将结果存储在对象中,并在完成时将PostMessage()对象(强制转换为lParam)返回到主线程,以便在消息中显示结果的GUI-处理程序,(再次抛出lParam)。然后,主GUI线程中的后台线程永远不必对同一个对象进行操作,也不必直接访问彼此的任何字段。

我使用GUI线程的隐藏窗口(使用RegisterWindowClass和CreateWindow创建),用于PostMessage的后台线程,LParam中的comms对象和'target'TwinControl(通常是TForm类),作为WParam。隐藏窗口的普通wndproc只使用TwinControl.Perform()将LParam传递给表单的消息处理程序。这比将对象直接PostMessaging到TForm.handle更安全 - 遗憾的是,如果重新创建窗口,句柄会发生变化。隐藏窗口从不调用RecreateWindow(),因此它的句柄永远不会改变。

生产者 - 消费者队列'从GUI',线程间通信类/对象和PostMessage()'到GUI'将运行良好 - 我已经这样做了几十年。

重新使用comms对象也相当容易 - 只需在启动时在循环中创建一个加载(最好是在初始化部分,以便使所有表单的comms对象都活得更长),并将它们推送到PC队列 - 这就是你的池。如果comms类具有池实例的私有字段,则更容易 - 'releaseBackToPool'方法则不需要参数,如果有多个池,则确保始终将对象释放回自己的池。

3)无法真正改善David Hefferman的评论。只是不要这样做。

答案 2 :(得分:1)

您可以将线程之间的数据作为消息进行通信。

线程1:

  1. 为数据结构分配内存
  2. 填写
  3. 使用指向此结构的指针向Thread2发送消息(您可以使用Windows消息或实现队列,确保其enque和dequeue方法没有竞争条件)
  4. 可能会收到来自Thread2的响应消息......
  5. 线程2:

    1. 使用指向Thread1
    2. 的数据结构的指针接收消息
    3. 使用数据
    4. 释放数据结构的内存
    5. 可能以类似的方式将消息发送回Thread1(可能重用数据结构,但是你不会解除分配)
    6. 如果您希望GUI不仅可以使用,而且还可以响应某些输入,而您正在处理需要很长时间处理的输入,则最终可能会有超过1个非GUI线程。