Multithreading (FireDAC)
Nach oben zu Arbeiten mit Verbindungen (FireDAC)
In diesem Thema wird beschrieben, wie FireDAC in einer Multithread-Umgebung verwendet wird.
Inhaltsverzeichnis
Allgemeine Informationen
FireDAC ist Thread-sicher, wenn die folgenden Bedingungen erfüllt sind:
- Ein Verbindungsobjekt und alle zugeordneten Objekte (wie TFDQuery, TFDTransaction usw.) werden zu einem bestimmten Zeitpunkt nur von einem einzelnen Thread verwendet.
- FDManager wurde vor dem Start der Threads durch Setzen von FDManager.Active auf True gestartet.
Das bedeutet, dass nachdem ein Thread eine Abfrage geöffnet hat und bis deren Verarbeitung abgeschlossen ist, die Anwendung diese Abfrage und die Verbindungsobjekte nicht in einem anderen Thread verwenden kann. Genauso kann die Anwendung, nachdem ein Thread eine Transaktion gestartet hat und bis die Transaktion abgeschlossen ist, diese Transaktion und die Verbindungsobjekte nicht in einem anderen Thread verwenden.
Dies bedeutet praktisch, dass eine Anwendung den Zugriff auf eine Verbindung für alle Threads serialisieren muss, was sehr aufwendig sein kann. Werden diese Regeln nicht eingehalten, kann das zu Fehlverhalten, zu Zugriffsverletzungsfehlern und zu anderen Fehlern, wie dem SQL Server-Fehler "Die Verbindung ist mit Ergebnissen einer anderen Anweisung beschäftigt", führen.
Die Standardvereinfachung besteht im Erstellen und Verwenden eines zugehörigen Verbindungsobjekts zur Arbeit mit der Datenbank für jeden Thread. In diesem Fall ist keine zusätzliche Serialisierung erforderlich. Beispielsweise führt der folgende Code DB-Aufgaben in Threads aus:
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;
Hinweis: Verwenden Sie für den obigen Fall, wenn die Anwendung eine einzelne SQL-Abfrage im Hintergrund ausführt, den asynchronen Abfrageausführungsmodus.
Hinweis: Eine Multithread-Anwendung kann die in den Hintergrund-Threads geöffneten Verbindungen in einer Ereignisbehandlungsroutine für TFDManager.BeforeShutdown schließen, um mögliche Deadlocks zu vermeiden.
Verbindungs-Pooling
Eine der aufwendigen Datenbankinteraktionen ist die Herstellung einer Verbindung. In einer Multithread-Anwendung, in der jeder Thread startet, eine Verbindung herstellt, eine bestimmte kurze Aufgabe durchführt und die Verbindung freigibt, kann die wiederholte Verbindungseinrichtung zu Leistungseinbußen im gesamten System führen. Um dies zu vermeiden, kann die Anwendung das Verbindungs-Pooling verwenden.
Das Verbindungs-Pooling kann nur für eine persistente oder private Verbindungsdefinition durch Setzen von Pooled=True aktiviert werden. Für eine persistente Definition:
[Oracle_Pooled] DriverID=Ora Database=ORA_920_APP User_Name=ADDemo Password=a Pooled=True
oder für eine private Definition:
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;
In der Eigenschaft TFDConnection.Params können keine weiteren Parameter angegeben werden, weil alle in den Pool gestellten Verbindungen dieselben Verbindungsparameter gemeinsam nutzen.
Durch Setzen von TFDConnection.Connected auf True wird eine physische Verbindung aus dem Pool abgerufen. Durch Setzen von TFDConnection.Connected auf False wird die physische Verbindung wieder im Pool freigegeben, aber die Verbindung bleibt geöffnet. Die Anwendung kann die Methode TFDManager.CloseConnectionDef aufrufen, um alle in den Pool gestellten physischen Verbindungen zu schließen und freizugeben:
FDManager.CloseConnectionDef('Oracle_Pooled');
oder den FireDAC-Treibermanager durch Aufruf der folgenden Methode schließen:
FDManager.Close;
Weitere Verbindungsdefinitionsparameter können zum Einrichten eines Pools angegeben werden:
Parameter | Parameter | Beispiel |
---|---|---|
POOL_CleanupTimeout | Die Dauer (ms) bis FireDAC die Verbindungen entfernt, die länger als POOL_ExpireTimeout Millisekunden nicht mehr verwendet wurden. Der Vorgabewert beträgt 30.000 Millisekunden (30 Sek.). | 3600000 |
POOL_ExpireTimeout | Die Dauer (ms), nach der die inaktive Verbindung aus dem Pool gelöscht und freigegeben werden kann. Der Vorgabewert beträgt 90.000 Millisekunden (90 Sek.). | 600000 |
POOL_MaximumItems | Die maximale Anzahl der Verbindungen im Pool. Wenn die Anwendung mehr Verbindungen benötigt, wird eine Exception ausgelöst. Der Vorgabewert ist 50. | 100 |
Siehe auch
Beispiele
- FireDAC IFDPhysConnection Pooling (Beispiel)
- FireDAC TFDConnection Pooling (Beispiel)