Multithreading (FireDAC)

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Arbeiten mit Verbindungen (FireDAC)


In diesem Thema wird beschrieben, wie FireDAC in einer Multithread-Umgebung verwendet wird.

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