Delphi 多线程下的 ADO 编程
前言: 几个月前接到一个任务:将一后台程序访问数据库的方式从 BDE 改为 ADO,原因是由于业务量的增加,通过 BDE 不论是向数据库写入数据还是从数据 库中读出数据的速度都变得无法忍受,大家都知道 ADO 在数据库访问速度方面 比 BDE 要快的多了(我写了一个测试程序使用 ADO 比使用 BDE 快了近 100 倍!)。这个任务还不简单嘛,只要将 BDE 的控件更换成 ADO 的再修改一些代码 不就搞定了!我当时确实是这么想的,而且用了不到一个小时就搞定,测试运 行一段没问题,大功告成了,我想。谁知道一个恶梦就此开始,我的愚昧无知 使我在程序中埋下了一个超级炸弹,它的威力不次于 9.11 撞击世贸大厦的两架 客机,整个系统被它无情的催跨。程序在运行很长一段时间候捕获到一系列的 异常: OLE error 800A0E7F Access violation at address 00135770. Write of address 005D8B78 Access violation at address 00178EC6. Read of address FFFFFFFF Access violation at address 1F499BDD in module 'msado15.dll'. Read of address 0000000C ……. 接下来我们的
系统就像世贸大厦一下悲壮的倒下了。
为什么? 为什么?程序在为改动之前使用 BDE 运行得好好的,我并没有更改程序的 结构啊?我十分的迷惑,当然要想解决问题一切都得从错误代码开始。 OLE error 800A0E7F:什么咚咚来的?它什么意思?什么原因引起的?我找 了半天也没有在我的系统里找到它的说明,好在现在网络发达,也许有人遇到 跟我一样的问题吧,于是我用 OLE error 800A0E7F 作为关键字搜了一下,嘿 嘿,果真被我找到了:
>0x800A0E7F Operation cannot be performed while executing
> asynchronously.
异步执行时操作不能被执行(完成),还是不太清楚错误的原因,于是我在一 个网站发布了帖子求助,一些人告诉我 ADO 线程不安全,需要线程同步,事实 上我的程序做了同步,而且针对不同的应用使用了多个 ADOConnection,我想 我应该自己动手来好好研究一下这个问题了,它很意思。接下来我该好好分析 我的程序并做一系列的测试来找到那个炸弹。
找出炸弹
在我的程序里所有访问数据库都是通过一个 DataModule 单元 TDataModule1 类提供的接口来完成,共有三个线程使用到了 TDataModule1 的 对象 DataModule1,DataModule1 是一全局变量,下面是数据库的访问模式的结 构模型图。(实际结构要复杂很多)
①UpdateQuery 数据库: ①ADOQuery1 ADOConnection1 Table1
③ADOQuery2
②ADOQuery3 ADOConnection2 ① ADOProcedure1 ① ADOProcedure2
Table2
图1 说明: ①UpdateQuery ADOQuery 控件用来修改 table2 记录,①代表为线程 1 所 有, 白色代表使用频率很低(颜色越深说明使用频率越 高)
③ADOQue
ry2 查询 table2,③代表为线程 3 所有,使用频率较高
②ADOQuery3
查询 table2,③代表为线程 2 所有,使用频率很高
① ADOProcedure1 频繁使用
ADO 存储过程控件向表 table2 插入数据,属于线程 1
① ADOProcedure2
修改 ADOProcedure1 插入的记录,属于线程 1 频繁使用
其中线程 3 和线程 2 使用 ADO 控件时没有加锁,而线程 1 的所有访问都加锁了 (这样做毫无作用)
程序的结构出来了,问题在哪里呢?接下来我写了一个小小的测试
程序,该程 序的结构与上面相同,它拥有三个线程和一个 DataMoule 单元,线程一通过 ADOQuery1 查询数据库 DBTest 的 table1 的记录,线程二通过 ADOQuery2 向 table1 中插入记录,线程三通过 ADOQuery3 修改 table1 中最后一条记录的某 个字段。ADOQuery1、ADOQuery2、ADOQuery3 都通过 ADOConnection1 与数据库 DBTest1 建立连接,一开始,所有的线程都不做同步,运行,OK!错误出来了其 中两个错误正是我所想要的,这就是我的程序报的错啊。
图二
接下来我将三个 ADOQuery 都加上锁,再运行没问题