浅谈多线程访问access数据库处理

来源:互联网 发布:数据库查询多少人重名 编辑:程序博客网 时间:2024/06/12 00:57

        delphi下面用ADO连接access数据库,多线程访问数据库会发现如果与数据库交互频繁会发现程序会弹出一些莫名其妙的错误,如“内存访问错误”、“在异步运行时,操作不能执行”等莫名其妙的错误,为什么会出现这种情况呢?

为什么?

        ADO控件是线程不安全的,所以如果你的程序是使用多线程访问数据库的话你应该确保每个线程都有自己的会话。ADO的数据操作默认是异步,就是你可以连续发送几条指令给服务器,但是在返回结果之前,你改变了客户端状态,服务器会产生错误。具体你看看ADO的帮助。

怎么解决?       

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

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

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

        因此对于大型数据库如SQL Server、Oracle等在多线程访问数据库时可以直接每个线程创建一个ADOConnection,从而避免资源冲突。

        但是对于access这种文件型数据库来说,因为数据库本身没有多线程处理机制,只能一条一条SQL语句来执行,因此如果每个线程都创建一个ADOConnection的话还是无法避免资源冲突,还需要保证同一时刻只能有一条SQL语句执行,因此需要多数据库操作加锁来保证执行的唯一性,而同时线程创建多个ADOConnection也没有必要了,因此对于access等文件型数据库来说,多线程访问数据库只需要对数据库操作加锁保证执行的唯一性既能解决该问题。

        数据库操作如下:

var  CSOperationDB:TRTLCriticalSection; //全局临界区变量InitializeCriticalSection(CSOperationDB);  //初始化数据库临界区//在程序关闭时需要执行DeleteCriticalSection(CSOperationDB)来删除数据库临界区procedure LockDB; //进入操作数据库临界区begin  EnterCriticalSection(CSOperationDB);end;procedure UnLockDB;begin  LeaveCriticalSection(CSOperationDB);end;function TDMMain.RunQuery(sSql: string): TDataSet;var  sdsTmp: TADODataSet;begin  result := nil;  if sSql='' then    exit;  sdsTmp := TADODataSet.Create(self);  sdsTmp.Connection := ADOConn;  sdsTmp.Close;  sdsTmp.ParamCheck := false;  sdsTmp.CommandType := cmdText;  sdsTmp.CommandText := sSql;  try    try      LockDB;      sdsTmp.Open;    finally      UnLockDB;    end;    result := sdsTmp;  except    sdsTmp.Free;  end;end;function TDMMain.CloseQuery(dsSource: TDataSet): integer;begin  if dsSource = nil then  begin    result := 0;    exit;  end;  try    dsSource.Free;    result := 0;  except    result := -1;  end;end;function TDMMain.ExecCmd(sCmd: string): boolean;begin  result := false;  if sCmd = '' then  begin    result := true;    exit;  end;    dsCmd.Close;//dsCmd,一个TADOQuery控件,不需要动态创建  dsCmd.SQL.Text := sCmd;  try    try      LockDB;      dsCmd.ExecSQL;    finally      UnLockDB;    end;    result := true;  except    //记录日志文件  end;end;