我正在创建一个访问数据库的应用程序。在每次访问数据库时,应用程序都会等待作业完成。 为了保持UI响应,我想将所有数据库内容放在一个单独的线程中 这是我的想法:
听起来不错吗? 将db-thread的数据库结果导入主线程的最佳方法是什么? 到目前为止,我对线程做的并不多,因此我想知道db-thread是否可以创建一个查询组件,主线程读取结果。主线程和数据库线程永远不会同时访问查询。这还会导致问题吗?
答案 0 :(得分:6)
您正在寻找的是标准数据访问技术,称为异步查询执行。一些数据访问组件以易于使用的方式实现此功能。至少dbGo(ADO)和AnyDAC实现了这一点。让我们考虑一下dbGo。
这个想法很简单 - 你可以调用方便的数据集方法,比如Open。该方法在后台线程中启动所需任务并立即返回。任务完成后,将触发相应的事件,通知应用程序,任务已完成。
使用DB GUI应用程序和Open方法的标准方法如下(草稿):
此外,您的“等待”对话框可能还有一个“取消”按钮,用户可以使用该按钮取消运行时间过长的查询。
答案 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 ...
祝你好运