2017-05-27 16 views
3

現在、マルチスレッドサーバーアプリケーションで作業中です。データアクセスにFiredacを使用する予定です。ここに提供されているドキュメント:http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Multithreading_(FireDAC)から、同じTFDConnectionおよび/またはTFDQueryに複数のスレッドから同時にアクセスしないでください(代わりに、これらのオブジェクトはスレッドごとに作成する必要があります)。マルチスレッドアプリケーションでのFiredacの使用

したがって、前のリンクに示した例では、TFDConnectionTFDQueryTThreadオブジェクトに集中しています。しかし、私の場合、私はスレッドの作成(サーバー環境によって管理される)のコントロールを持っていません。このアプローチは有効な

procedure TAPMFiredacTemplate.executeSQL(sql:string); 
    var 
    oConn: TFDConnection; 
    oQuery: TFDQuery; 
    begin 
    oConn := TFDConnection.Create(nil); 
    oQuery := TFDQuery.Create(nil); 
    try 
    oConn.ConnectionDefName := self.ConnectionDefinitionName; 
    oConn.Connected := True; 
    oQuery.Connection := oConn; 
    oQuery.Open(sql); 
    while not oQuery.Eof do 
    begin 
     // process query 
     oQuery.Next; 
    end; 

    finally 
    if assigned(oQuery) then 
    begin 
     oQuery.Close; 
     oQuery.Free; 
    end; 
    if assigned (oConn) then 
    begin 
     oConn.Connected := False; 
     oConn.Free; 
    end; 

    end; 

です:私は、したがって、潜在的に複数のスレッドから呼び出すことができる手続きの寿命に私のTFDConnectionTFDQueryオブジェクトのライフサイクルを制限するのですか? TFDQueryオブジェクトを体系的に作成すると、パフォーマンスが低下しますか?

注:パフォーマンスを向上させるために、プライベートプール接続定義(TFDConnectionで使用)を使用する予定です。だから私はTFDConnectionを解放しても私の理解から、物理的な接続が破壊されるのではなく、プールに返さ:

oParams := TStringList.Create; 
oParams.Add('Database=localhost:c:\apm\databases\mydb.FDB'); 
oParams.Add('User_Name=xxxxx'); 
oParams.Add('Password=xxxxxx'); 
oParams.Add('Pooled=True'); 
FDManager.AddConnectionDef('Firebird_Pooled','FB',oParams); 
FDManager.Active := True; 
+1

最初のアプローチのためのパフォーマンスペナルティがあります。 [接続プーリング](http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Multithreading_(FireDAC)#Connection_Pooling)に続きます。 [例題](http://docwiki.embarcadero.com/CodeExamples/Tokyo/en/FireDAC.Pooling_Sample)を確認することができます。 – Victoria

+0

私は 'FDManager'を使って事前に接続プールを作成し、そのプーリングされた接続を自分の手続きで使用することを考えていました(補足を参照)。つまり、私がTFDC接続を作成/解放しても、物理的な接続やプールからの接続です。これにより、パフォーマンスが向上します。しかし、私は 'TFDQuery'を最適化するために何ができるのかよく分かりません。 – BigONotation

+1

クエリを準備することは別の高価な操作になります。サーバーがキープアライブの種類の接続をサポートしている場合は、クエリオブジェクトを作成し、クライアントの接続時にクエリを準備し、切断時にクエリを破棄します。各リクエストは、準備されたクエリーオブジェクト(コンテキストクラスによって参照される)で処理できます。 – Victoria

答えて

2

それはスレッドコンテキストの実行のための有効なアプローチであるが、それは、データベース接続のパフォーマンスが低下しています各クライアント要求の準備とクエリ(Indyサーバーを使用していると仮定します)。

最初の問題を解決するには、connection poolingthe exampleに従うことができます)を使用してください。

後者の問題を解決するには解決策もあります。あなたのサーバが生きている種類の接続をサポートしている場合は、クライアントが接続するときにクエリオブジェクトを作成し、接続が切断されたときにはprepare the queryを作成して破棄します。この準備されたオブジェクトは、拡張コンテキスト・クラスを介してサーバー要求処理メソッドに渡すことができます。 TIdTCPServerと例えば

それは次のようになります。

type 
    { server context in the meaning of a "task" class } 
    TMyContext = class(TIdServerContext) 
    private 
    FQuery: TFDQuery; 
    public 
    constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); override; 
    destructor Destroy; override; 
    property Query: TFDQuery read FQuery; 
    end; 

    TForm1 = class(TForm) 
    IdTCPServer1: TIdTCPServer; 
    FDConnection1: TFDConnection; 
    procedure FormCreate(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
    procedure IdTCPServer1Connect(AContext: TIdContext); 
    procedure IdTCPServer1Disconnect(AContext: TIdContext); 
    procedure IdTCPServer1Execute(AContext: TIdContext); 
    procedure IdTCPServer1Exception(AContext: TIdContext; AException: Exception); 
    end; 

implementation 

constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); 
begin 
    inherited; 
    FQuery := TFDQuery.Create(nil); 
end; 

destructor TMyContext.Destroy; 
begin 
    FQuery.Free; 
    inherited; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
var 
    Params: TStrings; 
begin 
    Params := TStringList.Create; 
    try 
    Params.Add('Database=localhost:C:\MyDatabase.fdb'); 
    Params.Add('User_Name=xxxxx'); 
    Params.Add('Password=xxxxx'); 
    Params.Add('Pooled=True'); 
    { add the definition to the global connection manager singleton } 
    FDManager.AddConnectionDef('FirebirdPooled', 'FB', Params); 
    finally 
    Params.Free; 
    end; 

    { use the added definition and establish the connection to the DB } 
    FDConnection1.ConnectionDefName := 'FirebirdPooled'; 
    FDConnection1.Connected := True; 

    { setup the context class, add a port binding and start the TCP server } 
    IdTCPServer1.ContextClass := TMyContext; 
    IdTCPServer1.Bindings.Add.Port := 6000; 
    IdTCPServer1.Active := True; 
end; 

procedure TForm1.FormDestroy(Sender: TObject); 
begin 
    { stop the TCP server and destroy all pooled physical connections } 
    IdTCPServer1.Active := False; 
    FDManager.CloseConnectionDef('FirebirdPooled'); 
end; 

procedure TForm1.IdTCPServer1Connect(AContext: TIdContext); 
begin 
    { client just connected, assign to the context query object the pooled 
    connection and a command text } 
    TMyContext(AContext).Query.Connection := FDConnection1; 
    TMyContext(AContext).Query.SQL.Text := 'SELECT * FROM MyTable WHERE ID=:ID'; 
    { preparing the query will acquire one physical connection from the pool 
    as this method internally opens the connection } 
    TMyContext(AContext).Query.Prepare; 
end; 

procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext); 
begin 
    { client just disconnected, return the physical connection to the pool } 
    TMyContext(AContext).Query.Connection.Close; 
end; 

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext); 
var 
    ID: Integer; 
    Query: TFDQuery; 
begin 
    { read an integer from socket } 
    ID := AContext.Connection.IOHandler.ReadInteger; 
    { just a reference helper } 
    Query := TMyContext(AContext).Query; 

    { fill the parameter and refresh the prepared query object's dataset } 
    Query.Params[0].AsInteger := ID; 
    Query.Refresh; 

    while not Query.Eof do 
    begin 
    { process the dataset somehow } 
    Query.Next; 
    end; 

    { do not close the dataset, keep it prepared for the next possible request } 
end; 

procedure TForm1.IdTCPServer1Exception(AContext: TIdContext; AException: Exception); 
begin 
    if AException is EFDException then 
    begin 
    { something bad happened with the DB, this is a base FireDAC exception 
     class but you can be more specific of course } 
    end; 
end; 
関連する問題