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. Readof address FFFFFFFF

Access violation at address 1F499BDD inmodule ‘msado15.dll‘. Read of address 0000000C

接下来我们的系统就像世贸大厦一下悲壮的倒下了。

为什么?

为什么?程序在为改动之前使用BDE运行得好好的,我并没有更改程序的结构啊?我十分的迷惑,当然要想解决问题一切都得从错误代码开始。

OLE error 800A0E7F:什么咚咚来的?它什么意思?什么原因引起的?我找了半天也没有在我的系统里找到它的说明,好在现在网络发达,也许有人遇到跟我一样的问题吧,于是我用OLE error 800A0E7F作为关键字搜了一下,嘿嘿,果真被我找到了:

>0x800A0E7FOperation cannot be performed while executing 
> asynchronously.

异步执行时操作不能被执行(完成),还是不太清楚错误的原因,于是我在一个网站发布了帖子求助,一些人告诉我ADO线程不安全,需要线程同步,事实上我的程序做了同步,而且针对不同的应用使用了多个ADOConnection,我想我应该自己动手来好好研究一下这个问题了,它很意思。接下来我该好好分析我的程序并做一系列的测试来找到那个炸弹。

找出炸弹 

在我的程序里所有访问数据库都是通过一个DataModule单元TDataModule1类提供的接口来完成,共有三个线程使用到了TDataModule1的对象DataModule1,DataModule1是一全局变量,下面是数据库的访问模式的结构模型图。(实际结构要复杂很多)

图1

说明:

UpdateQuery    ADOQuery控件用来修改table2记录,①代表为线程1所有,

白色代表使用频率很低(颜色越深说明使用频率越高)

ADOQuery2   查询table2,③代表为线程3所有,使用频率较高

ADOQuery3 查询table2,③代表为线程2所有,使用频率很高

ADOProcedure1   ADO存储过程控件向表table2插入数据,属于线程1频繁使用

修改ADOProcedure1插入的记录,属于线程1频繁使用

其中线程3和线程2使用ADO控件时没有加锁,而线程1的所有访问都加锁了(这样做毫无作用)

程序的结构出来了,问题在哪里呢?接下来我写了一个小小的测试程序,该程序的结构与上面相同,它拥有三个线程和一个DataMoule单元,线程一通过ADOQuery1查询数据库DBTest的table1的记录,线程二通过ADOQuery2向table1中插入记录,线程三通过ADOQuery3修改table1中最后一条记录的某个字段。ADOQuery1、ADOQuery2、ADOQuery3都通过ADOConnection1与数据库DBTest1建立连接,一开始,所有的线程都不做同步,运行,OK!错误出来了其中两个错误正是我所想要的,这就是我的程序报的错啊。

图二

接下来我将三个ADOQuery都加上锁,再运行没问题,我又将ADOQuery分别通过三个不同的ADOConnection来连接数据库且不加锁也没有问题。看来我是找到那个可恶的炸弹了,怎么拆了它?

排除炸弹 

炸弹找到了,我该怎么拆它?是简单的做线程同步还是每个线程都是用一个ADOConnection?这下我再也不敢蛮干了,我得好好看看这方面的资料,在Delphi帮助文档,《Usingthe main VCL thread》我找到了下面一段话:

Data access components are thread-safe as long as each thread hasits own database session component. The one exception to this is when you areusing Access drivers. Access drivers are built using the Microsoft ADO library,which is not thread-safe.

同样在Delphi的帮助文档《Managingmultiple sessions》中给我明确的建议:

If you create a single application that uses multiple threads toperform database operations, you must create one additional session for eachthread.

喔找到了:ADO控件是线程不安全的,所以如果你的程序是使用多线程访问数据库的话你应该确保每个线程都有自己的会话。

事实上在另外一本书《Delphi 4编程技术内幕》一书在谈到线程安全数据库访问也有相同的建议,不过台湾李维先生在他的《Delphi 5.X ADO/MTS/COM+高级程序设计篇》却说,如果你的程序不是连接多个数据库的话,最好同一数据库使用一个连接,不要使用多个连接。怎么办?谁对谁错?为什么要使用一个连接呢?这主要是从服务器来考虑,因为数据库服务器需要为每个连接分配一定的资源并对其进行维护,连接数越多服务器方所耗的资源就越多,服务器的性能也就越差,所以要尽可能的减少客户端的连接数。好在我的程序是作为服务器程序增加一些连接对数据库服务器的影响不会很大,现在我可以重新设置我的数据库访问结构模型了

图三

我增加了一个ADOConnection以保证每个线程都有一个自己连接(会话),从而避免出现资源冲突,我的问题是不是解决了呢?是的,这个问题已经解决了,将我的程序与数据库放在同一台机器上运行没有问题,但是当程序与数据库服务器不在同一台机器上运行时会出现一个新的问题。

[DBNMPNTW]ConnectionWrite(writeFile())错误

这个错误不是多线程引起的,而是Micrsoft自己的一个问题,产生该问题的原因可能是因为网络异常而引起的,可以通过SQLServer客户端的默认的网络协议namedpipes network propocol 改为 TCP/IP Sockets,具体做法请参考Micrsoft技术支持网站的《Microsoft Knowledge Base Article - Q178040

总结

   由于ADO控件的线程不安全性(事实上这种不安全性是来自Micrsoft ADO Library,所以在其它开发工具中也存在同样的问题)因此在使用多线程ADO编程时应该注意一下问题:

第一:要保证每个线程都拥有自己的会话。

第二:作为客户端程序应该尽可能的减少与数据库库服务器的连接数。

第三:在退出线程之前确保释放所有的资源。

 

参考文献:

1、李维《Delphi 5.X ADO/MTS/COM+高级程序设计篇》机械工业出版社 2000。

2、CharlieCalvert《Delphi4编程技术内幕》潇湘工作室 译 机械工业出版社 1999。

http://blog.csdn.net/youthon/article/details/8890967

时间: 2024-10-12 11:31:45

Delphi多线程下的ADO编程的相关文章

delphi 多线程 动态创建ADO

delphi 在多线程中动态连接ADO控件 异常:exception class EOleSysError with message '尚未调用CoInitialize' 如果是使用多线程的话那就在 Execute事件的开头加上 CoInitialize(nil); 结尾加上CoUninitialize() unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Fo

DELPHI下的SOCK编程

本文是写给公司新来的程序员的,算是一点培训的教材.本文不会涉及太多的编程细节,只是简单讲解在DELPHI下进行Winsock编程最好了解的知识. 题外话:我认为学习编程就如同学习外语一样,最好的方式是你先学会如何去运用它,然后才是了解它的语言特性.语法之类的东西.不过很可惜,我们以前的外语教育使用了相反的过程.软件编程也是一样,在很多人的大学阶段,你更多的是学习那些理论知识,学习“语法”,这里,我丝毫没有贬低理论知识重要性的意思.理论知识和实践是相辅相成的,但一个恰当的学习方式,很多时候可以让学

深入Delphi下的DLL编程

深入Delphi下的DLL编程 作者:岑心 引 言 相信有些计算机知识的朋友都应该听说过“DLL”.尤其是那些使用过windows操作系统的人,都应该有过多次重装系统的“悲惨”经历——无论再怎样小心,没有驱动损坏,没有病毒侵扰,仍然在使用(安装)了一段时间软件后,发现windows系统越来越庞大,操作越来越慢,还不时的出现曾经能使用的软件无法使用的情况,导致最终不得不重装系统.这种情况常常是由于dll文件的大量安装和冲突造成的.这一方面说明DLL的不足,另一方面也说明DLL的重要地位,以至我们无

多线程下SOCKET编程

SOCKET编程往往离不开多线程,说到多线程,总的说来还是一个比较复杂的东西,尤其是业务逻辑变得复杂的时候,有时候或者说经常,会弄得人头晕脑胀.但是C#,相对好一点,因为微软封装了,帮我们做很多工作,但是在实际的编码过程中,往往会出现各种各样的错误或者BUG. 这里先写一个简单的多线程函数,这样有个初步的理解,也算是入门了,相信大家不用多论述就能明白. public static void Start() { Thread t1 = new Thread(ExceMethod); t1.IsBa

DELPHI下的SOCK编程(转)

DELPHI下的SOCK编程      本文是写给公司新来的程序员的,算是一点培训的教材.本文不会涉及太多的编程细节,只是简单讲解在DELPHI下进行Winsock编程最好了解的知识. 题外话:我认为学习编程就如同学习外语一样,最好的方式是你先学会如何去运用它,然后才是了解它的语言特性.语法之类的东西.不过很可惜,我们以前的外语教育使用了相反的过程.软件编程也是一样,在很多人的大学阶段,你更多的是学习那些理论知识,学习“语法”,这里,我丝毫没有贬低理论知识重要性的意思.理论知识和实践是相辅相成的

Cpython解释器下实现并发编程——多进程、多线程、协程、IO模型

一.背景知识 进程即正在执行的一个过程.进程是对正在运行的程序的一个抽象. 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所有内容都是围绕进程的概念展开的.   一.操作系统相关的知识 详情见链接:http://www.cnblogs.com/linhaifeng/p/6295875.html 即使可以利用的CPU只有一个(早期的计算机确实如此),也能保证支持(伪)并发的能力.将一个单独的CPU变成多个虚拟的CPU(多道技术:时

Window下高性能IOCP模型队列多线程下应用

IOCP,先从概念上认识一下.IOCP全称I/O Completion Port,中文译为I/O完成端口.是Windows平台最高效的I/O模块,现在IIS服务器,就采用IOCP模型.IOCP是一个异步I/O的API,它可以高效地将I/O事件通知给应用程序.与使用select()或是其它异步方法不同的是,现在很多书,文字都直接将IOCP模块和网络编程关联起来,好像IOCP就是和网络打交道的.典型的IOCP模型的使用,是 将一个套接字(socket)与一个完成端口关联了起来,当一个网络事件发生的时

多线程下C#如何保证线程安全?

多线程编程相对于单线程会出现一个特有的问题,就是线程安全的问题.所谓的线程安全,就是如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码.如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的. 线程安全问题都是由全局变量及静态变量引起的. 为了保证多线程情况下,访问静态变量的安全,可以用锁机制来保证,如下所示: 1 //需要加锁的静态全局变量 2 private static bool _isOK = false; 3 //lock只能锁定一

Visual C++中的ADO编程

摘  要:本文介绍了微软推出的以ActiveX技术为基础的ADO数据存取技术,分析了ADO的工作原理,并用Visual C++说明了ADO数据访问技术的实现方法. 关键字:ADO:连接对象:OLE DB:数据存取:记录集 ADO(ActiveX Date Object)是Microsoft数据库应用程序开发的新接口,是建立在OLE DB之上的高层数据库访问技术.ADO 技术基于COM(Component Object Model),具有COM组件的诸多优点,可以用来构造可复用应用框架,被多种语言