マルチスレッド処理(FireDAC)
接続の操作(FireDAC) への移動
このトピックでは、マルチスレッド環境での FireDAC の使用方法を説明します。
概要
FireDAC は、以下の条件を満たしている場合はスレッドセーフです。
- 接続オブジェクトとそれに関連付けられているすべてのオブジェクト(たとえば TFDQuery、TFDTransaction など)が、それぞれの瞬間に単一のスレッドで使用される。
- スレッドを開始する前に FDManager をアクティブにするには、FDManager.Active を True に設定します。
つまり、アプリケーションでは、スレッドでクエリが開始されてから、その処理が完了するまでは、そのクエリと接続オブジェクトを別のスレッドで使用できないということです。 同様に、アプリケーションでは、スレッドでトランザクションが開始されてから、そのトランザクションが完了するまでは、そのトランザクションと接続オブジェクトを別のスレッドで使用できません。
そうなると、実質的に、アプリケーションでは接続へのアクセスをすべてのスレッドにわたってシリアル化する必要があるということになりますが、それは便利な手法ではありません。 これらのルールに違反すると、誤動作、アクセス違反エラー、その他のエラー(たとえば 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 イベント ハンドラで閉じることで、発生するおそれのあるデッドロックを避けることができます。
接続のプール
コストのかかるデータベース操作の 1 つは接続の確立です。 マルチスレッド アプリケーションでは、開始したスレッドごとに接続を確立し、ある短時間の処理を実行し、その接続を解放するため、接続の確立を繰り返すと、システム全体にわたってパフォーマンスが低下するおそれがあります。 これを避けるには、アプリケーションで接続のプールを使用することができます。
永続接続定義または非公開接続定義の場合にのみ、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;
以下の追加の接続定義パラメータを指定して、プールをセットアップすることもできます。
パラメータ | パラメータ | 例 |
---|---|---|
POOL_CleanupTimeout | POOL_ExpireTimeout で指定された時間より長く使用されていない接続を FireDAC が削除するまでの時間(ミリ秒単位)。 デフォルト値は 30000 ミリ秒(30 秒)です。 | 3600000 |
POOL_ExpireTimeout | アクティブでない接続がプールから削除され破棄されるまでの許容時間(ミリ秒単位)。 デフォルト値は 90,000 ミリ秒(90 秒)です。 | 600000 |
POOL_MaximumItems | プール内の接続の最大数。 アプリケーションでそれ以上の接続を要求した場合は、例外が発生します。 デフォルト値は [50] です。 | 100 |