参见:Delphi 中 FireDAC 数据库连接(总览)
本主题描述了如何在多线程环境下使用FireDAC。
一、概述
如果满足以下条件,FireDAC是线程安全的。
一个连接对象和所有与之相关的对象(如TFDQuery、TFDTransaction等)在每个时刻都由一个线程使用。
FDManager在线程开始之前被激活,通过设置FDManager.Active为True。
这意味着,在一个线程打开一个查询后,直到它的处理完成,应用程序不能在另一个线程中使用这个查询和连接对象。同样,在一个线程启动一个交易后,直到交易完成,应用程序不能在另一个线程中使用这个交易和连接对象。
这实际上意味着应用程序必须在所有线程中序列化对连接的访问,这并不是一种方便的技术。破坏这些规则可能会导致行为不当、AV错误和其他错误,例如SQL Server的错误 "连接正忙于处理另一条命令的结果"。
标准的简化方法是为每个线程创建和使用一个与数据库工作的专用连接对象。在这种情况下,不需要额外的序列化。例如,下面的代码在线程中执行DB任务。
type
TDBThread = class(TThread)
protected
procedure Execute; override;
end;
procedure TDBThread.Execute;
var
oConn: TFDConnection;
oPrc: TFDQuery;
begin
FreeOnTerminate := False;
oConn := TFDConnection.Create(nil);
oConn.ConnectionDefName := 'Oracle_Pooled'; // see next section
oPrc := TFDStoredProc.Create(nil);
oPrc.Connection := oConn;
try
oConn.Connected := True;
oPrc.StoredProcName := 'MY_LONG_RUNNING_PROC';
oPrc.ExecProc;
finally
oPrc.Free;
oConn.Free;
end;
end;
// main application code
var
oThread1, oThread2: TDBThread;
begin
FDManager.Active := True;
...
oThread1 := TDBThread.Create(False);
oThread2 := TDBThread.Create(False);
...
oThread1.WaitFor;
oThread1.Free;
oThread2.WaitFor;
oThread2.Free;
end;
注意:对于上述情况,应用程序在后台运行一个单一的SQL查询,使用异步查询执行模式。一个多线程的应用程序可以在TFDManager.BeforeShutdown事件处理程序中关闭在后台线程中打开的连接,以避免可能的死锁。
二、连接池
费时的数据库交互操作之一是建立连接。在一个多线程的应用程序中,每个线程都会启动,建立一个连接,执行某个简短的任务并释放连接,重复的连接建立可能导致整个系统的性能下降。为了避免这种情况,应用程序可以使用连接池。
连接池只能通过设置Pooled=True来为持久化或私有连接定义启用。对于一个持久化的定义。
[Oracle_Pooled]
DriverID=Ora
Database=ORA_920_APP
User_Name=ADDemo
Password=a
Pooled=True
或用于私有定义设置。
var
oParams: TStrings;
begin
oParams := TStringList.Create;
oParams.Add('Database=ORA_920_APP');
oParams.Add('User_Name=ADDemo');
oParams.Add('Password=a');
oParams.Add('Pooled=True');
FDManager.AddConnectionDef('Oracle_Pooled', 'Ora', oParams);
.....................
FDConnection1.ConnectionDefName := 'Oracle_Pooled';
FDConnection1.Connected := True;
不能在TFDConnection.Params属性中指定额外的参数,因为所有池中的连接必须共享相同的连接参数。
将TFDConnection.Connected设置为True会从池中获取一个物理连接。将TFDConnection.Connected设置为False会将物理连接释放到池中,但保持连接的开放。要关闭和销毁所有池中的物理连接,应用程序可以调用TFDManager.CloseConnectionDef方法。
FDManager.CloseConnectionDef('Oracle_Pooled');
或通过调用关闭FireDAC驱动程序管理器。
FDManager.Close;
可以指定额外的连接定义参数来设置一个池:
Parameter | Parameter | Example |
POOL_CleanupTimeout | 直到FireDAC删除超过POOL_ExpireTimeout时间未使用的连接的时间(毫秒)。默认值是30000msecs(30秒)。 | 3600000 |
POOL_ExpireTimeout | 不活动连接从池中删除并销毁的时间(msecs)。默认值是90000msecs(90秒)。 | 600000 |
POOL_MaximumItems | 池中的最大连接数。当应用程序需要更多的连接时,就会产生一个异常。默认值是50。 | 100 |
关于Delphi 连接池的示例程序,请参见:
你可以在以下位置找到TFDConnection/Pooling示例项目。
Start | Programs | Embarcadero RAD Studio Sydney | Samples,然后导航到。
Object Pascal\DataBase\FireDAC\Samples\Comp Layer\TFDConnection\Pooling。
Subversion Repository:你可以在GitHub存储库中找到Delphi代码样本。根据你的RAD Studio版本,按名称搜索到样本库。