在单独的线程中连接数据库 - 最佳方式是什么?

4
我正在创建一个访问数据库的应用程序。每次访问数据库时,应用程序都会等待任务完成。 为了保持UI响应性,我想把所有数据库操作放在一个单独的线程中。
这是我的想法:
  • 当db-thread被创建时,它创建所有需要的数据库组件
  • 现在线程只是坐在那里等待命令
  • 如果它接收到一个命令,它执行该操作并返回空闲状态。此期间主线程等待。
  • db-thread的生命周期与应用程序运行的时间一样长
这听起来可以吗?
将数据库结果从db-thread传递到主线程的最佳方法是什么?
我之前没怎么使用过线程,因此我想知道db-thread是否可以创建一个查询组件,然后主线程读取结果。主线程和db线程永远不会同时访问查询。这样做是否会导致问题?
4个回答

6
您要找的是标准的数据访问技术,称为异步查询执行。一些数据访问组件以易于使用的方式实现了此功能。至少dbGo(ADO)和AnyDAC实现了这一点。我们先考虑一下dbGo。
思路很简单 - 您调用方便的数据集方法,例如Open。该方法在后台线程中启动所需的任务并立即返回。完成任务时,将触发适当的事件,通知应用程序任务已完成。
DB GUI应用程序的标准方法是使用Open方法,具体如下:
- 将dataset ExecuteOptions包含eoAsyncExecute、eoAsyncFetch、eoAsyncFetchNonBlock; - 将TDataSource.DataSet与dataset断开连接; - 将数据集OnFetchComplete设置为一个proc P; - 显示“您好!我们正在努力处理您的请求,请稍等...”对话框; - 调用数据集Open方法; - 查询执行完成时,将调用OnFetchComplete,因此也将调用P。P隐藏“等待”对话框并将TDataSource.DataSet重新连接到数据集。
同时,“等待”对话框可能会有一个取消按钮,用户可以使用它来取消正在运行时间过长的查询。

5
首先,如果您没有多线程方面的经验,请不要从VCL类开始。使用OmniThreadLibrary,有以下原因:
- 您的抽象级别是任务,而不是线程,这是处理并发性的更好方式。 - 您可以轻松地在自己的线程中执行任务和使用线程池调度它们之间切换。 - 所有底层细节,如线程关闭、双向通信等都已为您处理。您可以专注于数据库操作。
“db-thread”创建所有需要的数据库组件时,这可能不是最佳方法。我通常只在需要时创建组件,但不立即销毁它们。您应该在线程池线程中保持连接打开,并仅在线程长时间处于非活动状态且池处理它时才关闭它。但通常也很明智地保留事务和语句对象的缓存。
第一部分在使用OTL时已经很好地处理了。然而,请不要让主线程等待,这与直接在VCL线程中执行数据库访问相比带来的优势很小。您需要异步设计才能充分利用多个线程。考虑一个标准的数据库浏览器表单,其中包含用于过滤记录的控件。我通过每次更改控件时(重新)启动计时器来处理此问题。一旦用户完成编辑,计时器事件触发(例如在500毫秒后),然后启动一个任务,执行根据过滤条件获取数据的语句。网格内容被清除,并且只有当任务完成时才重新填充。这可能需要一些时间,因此VCL线程不等待任务完成。相反,用户甚至可以再次更改过滤条件,在这种情况下当前任务将被取消并启动新任务。OTL为您提供了任务完成事件,因此异步设计易于实现。
我通常不使用数据感知组件进行多线程数据库应用程序,而是使用作为业务对象视图的标准控件。在数据库任务中,我创建这些对象,将它们放入列表中,然后任务完成事件将列表传输到VCL线程。
对于所有按需加载数据的组件,您无法确定会同时访问查询。通常仅从数据库中获取前几条记录,并在使用这些记录后继续获取。这些组件显然不应由线程共享。

2
如果您选择使用OTL,请确保阅读http://17slon.com/blogs/gabr/2009/02/building-connection-pool.html。 - gabr
感谢您提供详细的说明。我将从OmniThread库开始。 - Holgerwa

3

我已实现了线程池和即席线程创建两种策略。

我建议先从即席线程创建开始,这种方法更容易实现和扩展。

只有在(经过仔细评估后)(1)创建线程需要很多资源和时间,且(2)有大量创建请求时,才会使用线程池。

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

请参考线程使用的类、组件和函数的文档,确保它们是线程安全的,即它们可以同时从不同的线程中使用。如果不是,则需要同步访问。在某些情况下,您可能会发现关于线程安全性的轻微差异。例如,请参见DateTimeToStr。


1

如果您在开始时创建线程并在以后需要时重复使用它,则必须确保每次“处理”数据时从底层数据源(disableControls)断开db组件(grid..)。

为了简单起见,我会继承TThread并在自己的类中实现所有业务逻辑。结果数据集将是此类的成员,并且我将使用synchronize将其连接到db感知组件中。

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

祝你好运


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接