在单独的线程中进行数据库连接 - 最好的方法是什么?

时间:2010-02-18 21:29:19

标签: multithreading delphi delphi-2009

我正在创建一个访问数据库的应用程序。在每次访问数据库时,应用程序都会等待作业完成。 为了保持UI响应,我想将所有数据库内容放在一个单独的线程中 这是我的想法:

  • db-thread在创建时创建所需的所有数据库组件
  • 现在线程就坐在那里等待命令
  • 如果收到命令,它会执行操作并返回空闲状态。在那段时间内,主线程等待。
  • 只要应用程序正在运行,db-thread就会存在

听起来不错吗? 将db-thread的数据库结果导入主线程的最佳方法是什么? 到目前为止,我对线程做的并不多,因此我想知道db-thread是否可以创建一个查询组件,主线程读取结果。主线程和数据库线程永远不会同时访问查询。这还会导致问题吗?

4 个答案:

答案 0 :(得分:6)

您正在寻找的是标准数据访问技术,称为异步查询执行。一些数据访问组件以易于使用的方式实现此功能。至少dbGo(ADO)和AnyDAC实现了这一点。让我们考虑一下dbGo。

这个想法很简单 - 你可以调用方便的数据集方法,比如Open。该方法在后台线程中启动所需任务并立即返回。任务完成后,将触发相应的事件,通知应用程序,任务已完成。

使用DB GUI应用程序和Open方法的标准方法如下(草稿):

  • 将eoAsyncExecute,eoAsyncFetch,eoAsyncFetchNonBlock包含在数据集ExecuteOptions中;
  • 从数据集中断开TDataSource.DataSet;
  • 将数据集OnFetchComplete设置为proc P;
  • 显示“您好!我们会努力处理您的请求。请稍候......”对话框;
  • 调用数据集Open方法;
  • 当查询执行完成时,将调用OnFetchComplete,因此P.和P隐藏“等待”对话框并将TDataSource.DataSet连接回数据集。

此外,您的“等待”对话框可能还有一个“取消”按钮,用户可以使用该按钮取消运行时间过长的查询。

答案 1 :(得分:5)

首先 - 如果您没有多线程经验,请不要从VCL类开始。使用OmniThreadLibrary,以及(其中包括)这些原因:

  • 您的抽象级别是任务,而不是线程,是处理并发的更好方法。
  • 您可以轻松地在自己的线程中执行任务之间切换,并使用线程池安排它们。
  • 所有低级细节,如线程关闭,双向通信等等都会为您解决。你可以专注于数据库的东西。
  

db-thread在创建时创建所需的所有数据库组件

这可能不是最好的方法。我通常只在需要时才创建组件,但不会立即销毁。您绝对应该在线程池线程中保持连接打开,并且只有在线程处于非活动状态一段时间后才关闭它,并且池会处理它。但是保留事务和语句对象的缓存通常也是一个好主意。

  

如果收到命令,它会执行操作并返回空闲状态。在那段时间内,主线程等待。

使用OTL时,第一部分处理得很好。但是 - 没有主线程等待,这将比首先直接在VCL线程中执行数据库访问带来一点优势。您需要异步设计才能充分利用多个线程。考虑一个标准数据库浏览器表单,它具有过滤记录的控件。每当其中一个控件发生变化时,我会通过(重新)启动计时器来处理这个问题。一旦用户完成编辑定时器事件触发(例如在500毫秒之后),就会启动一个任务,执行根据过滤条件获取数据的语句。网格内容被清除,只有在任务完成后才会重新填充。这可能需要一些时间,因此VCL线程不会等待任务完成。相反,用户甚至可以再次更改过滤条件,在这种情况下,当前任务被取消并且新的任务开始。 OTL为您提供完成任务的事件,因此异步设计很容易实现。

  

将db-thread的数据库结果导入主线程的最佳方法是什么?

我通常不会将数据感知组件用于多线程数据库应用程序,而是使用标准控件作为业务对象的视图。在数据库任务中,我创建这些对象,将它们放在列表中,任务完成事件将列表传输到VCL线程。

  

主线程和数据库线程永远不会同时访问查询。

对于按需加载数据的所有组件,您无法确定这一点。通常只从db中获取第一个记录,并且在消耗之后继续提取。显然,这些组件不得由线程共享。

答案 2 :(得分:3)

我已经实现了两种策略:线程池和adhoc线程创建。

我建议从adhoc线程创建开始,它实现起来更简单,并且更容易扩展。

如果(仔细评估)(1)在创建线程时投入了大量资源(和时间),并且(2)您有大量创建请求,则只移至线程池。

在这两种情况下,您都必须处理传递参数并收集结果。我建议使用允许此数据传递的属性扩展线程类。

请参阅线程用于确保它们是线程安全的类,组件和函数的文档,也就是说,它们可以在不同的线程中同时使用。如果没有,您将需要同步访问。在某些情况下,您可能会发现线程安全性略有不同。例如,请参阅DateTimeToStr。

答案 3 :(得分:1)

如果您在开始时创建线程并在以后需要时重新使用它,则必须确保每次“处理”时都将db组件(grid ..)与基础数据源(disableControls)断开连接。数据

为了简单起见,我将继承TThread并在我自己的类中实现所有业务逻辑。结果数据集将是此类的成员,我将连接dbware comp in with synchronize。

无论如何,将尽可能多的工作委托给数据库服务器并使UI尽可能轻量化也非常重要。 Firebird是我最喜欢的数据库服务器:触发器,用于在Delphi中开发的选择性自定义UDF dll,许多线程安全的数据库组件,包含大量示例和良好支持(论坛):jvUIB ...

祝你好运